71_PHILIPS_AUDIO.pm 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683
  1. # $Id: 71_PHILIPS_AUDIO.pm 14036 2017-04-19 18:30:30Z ra666ack $
  2. ##############################################################################
  3. #
  4. # 71_PHILIPS_AUDIO.pm
  5. #
  6. # An FHEM Perl module for controlling Philips Audio Equipment connected to
  7. # local network such as MCi, Streamium and Fidelio devices.
  8. # The module provides basic functionality accessible through the port 8889
  9. # of the device: (http://<device_ip>:8889/index)
  10. # e.g. AW9000, NP3500, NP3700, NP3900
  11. #
  12. # Copyright by ra666ack
  13. # (e-mail: ra666ack at g**glemail d*t c*m)
  14. #
  15. # This file is part of fhem.
  16. #
  17. # Fhem is free software: you can redistribute it and/or modify
  18. # it under the terms of the GNU General Public License as published by
  19. # the Free Software Foundation, either version 2 of the License, or
  20. # (at your option) any later version.
  21. #
  22. # Fhem is distributed in the hope that it will be useful,
  23. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. # GNU General Public License for more details.
  26. #
  27. # You should have received a copy of the GNU General Public License
  28. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  29. #
  30. ##############################################################################
  31. package main;
  32. use strict;
  33. use warnings;
  34. use Time::HiRes qw(gettimeofday sleep);
  35. use Time::Piece;
  36. use POSIX qw{strftime};
  37. use HttpUtils;
  38. sub PHILIPS_AUDIO_Initialize
  39. {
  40. my ($hash) = @_;
  41. $hash->{DefFn} = "PHILIPS_AUDIO_Define";
  42. $hash->{GetFn} = "PHILIPS_AUDIO_Get";
  43. $hash->{SetFn} = "PHILIPS_AUDIO_Set";
  44. $hash->{AttrFn} = "PHILIPS_AUDIO_Attr";
  45. $hash->{UndefFn} = "PHILIPS_AUDIO_Undefine";
  46. $hash->{AttrList} = "do_not_notify:0,1 ".
  47. "disable:0,1 ".
  48. "autoGetPresets:0,1 ".
  49. "autoGetFavorites:0,1 ".
  50. "httpBufferTimeout ".
  51. "maxListItems ".
  52. "model ".
  53. "playerBrowsingTimeout ".
  54. "requestTimeout ".
  55. "$readingFnAttributes";
  56. return;
  57. }
  58. sub PHILIPS_AUDIO_GetStatus
  59. {
  60. my ($hash, $local) = @_;
  61. my $name = $hash->{NAME};
  62. my $power;
  63. $local = 0 unless(defined($local));
  64. return if((!defined($hash->{IP_ADDRESS})) or (!defined($hash->{helper}{OFF_INTERVAL})) or (!defined($hash->{helper}{ON_INTERVAL})));
  65. my $device = $hash->{IP_ADDRESS};
  66. # First run
  67. $hash->{helper}{networkRequest} = "idle" if (not defined($hash->{helper}{networkRequest}));
  68. # Try to get additional info from the device.
  69. # Only if device already implemented.
  70. # Otherwise possible timeout due to wrong ports and device description links
  71. if(not defined($hash->{helper}{dInfo}{UUID}))
  72. {
  73. if (grep {$_ eq $hash->{MODEL}} @{$hash->{helper}{DevDescImplementedModels}})
  74. {
  75. PHILIPS_AUDIO_getMediaRendererDesc($hash);
  76. }
  77. }
  78. if (not defined($hash->{helper}{playerState}))
  79. {
  80. # First run. Go to index.
  81. $hash->{helper}{playerState} = "home";
  82. readingsSingleUpdate($hash, "playerState", "home", 1);
  83. PHILIPS_AUDIO_SendCommand($hash, "/index", "","home", "noArg");
  84. PHILIPS_AUDIO_ResetTimer($hash); # getStatus
  85. return;
  86. }
  87. if($hash->{helper}{playerState} eq "home")
  88. {
  89. # Check if device playing. Might by activated by the remote control or app.
  90. # If not, the device returns 'NOTHING';
  91. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg");
  92. # Heartbeat
  93. #PHILIPS_AUDIO_SendCommand($hash, "/HOMESTATUS", "","homestatus", "noArg");
  94. readingsBeginUpdate($hash);
  95. readingsBulkUpdate($hash, "input", "-");
  96. readingsBulkUpdate($hash, "playerState", "home");
  97. readingsBulkUpdate($hash, "playerPlaying", "no");
  98. readingsEndUpdate($hash, 1);
  99. }
  100. elsif($hash->{helper}{playerState} eq "browsing")
  101. {
  102. # Do nothing and check for inactivity duration
  103. $hash->{helper}{playerBrowsingTimeout} = 0 if(not defined ($hash->{helper}{playerBrowsingTimeout}));
  104. $hash->{helper}{playerBrowsingTimeout} += $hash->{helper}{ON_INTERVAL};
  105. # reset browsing state after 3 minutes inactivity in order to update the readings automatically again
  106. if($hash->{helper}{playerBrowsingTimeout} >= int(AttrVal($name, "playerBrowsingTimeout", 180)))
  107. {
  108. $hash->{helper}{playerState} = "home";
  109. readingsBeginUpdate($hash);
  110. readingsBulkUpdate($hash, "input", "-");
  111. readingsBulkUpdate($hash, "playerState", "home");
  112. readingsBulkUpdate($hash, "playerPlaying", "no");
  113. readingsEndUpdate($hash, 1);
  114. $hash->{helper}{playerBrowsingTimeout} = 0;
  115. }
  116. }
  117. elsif($hash->{helper}{playerState} eq "playing")
  118. {
  119. PHILIPS_AUDIO_SendCommand($hash, "/ELAPSE", "","elapse", "noArg");
  120. }
  121. # Check for Presets availability
  122. if
  123. (
  124. (not defined($hash->{READINGS}{"totalPresets"})) and
  125. (ReadingsVal($name, "presence", "no") eq "present") and
  126. (AttrVal($name, "autoGetPresets", "0") eq "1")
  127. )
  128. {
  129. readingsSingleUpdate($hash, "Reading_presets" , "May take some time...", 1);
  130. # Hierarchichal navigation through the contents mandatory
  131. $hash->{helper}{cmdStep} = 1;
  132. PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getPresets", "noArg") if((ReadingsVal($name, "playerListStatus", "ready") eq "ready") and (ReadingsVal($name, "readingPresets", "no") eq "no"));
  133. PHILIPS_AUDIO_ResetTimer($hash, 10); # Scan takes approx 8 sec.
  134. return;
  135. }
  136. # Check for Favorites availability
  137. if
  138. (
  139. (not defined($hash->{READINGS}{"totalFavorites"})) and
  140. (ReadingsVal($name, "presence", "no") eq "present") and
  141. (AttrVal($name, "autoGetFavorites", "0") eq "1")
  142. )
  143. {
  144. readingsSingleUpdate($hash, "Reading_favorites" , "May take some time...", 1);
  145. # Hierarchichal navigation through the contents mandatory
  146. $hash->{helper}{cmdStep} = 1;
  147. PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getFavorites", "noArg") if((ReadingsVal($name, "playerListStatus", "ready") eq "ready") and (ReadingsVal($name, "readingFavorites", "no") eq "no"));
  148. PHILIPS_AUDIO_ResetTimer($hash, 10); # Scan takes approx 8 sec.
  149. return;
  150. }
  151. PHILIPS_AUDIO_ResetTimer($hash) if(not ($local == 1)); # getStatus
  152. return;
  153. }
  154. sub PHILIPS_AUDIO_Get
  155. {
  156. my ($hash, @a) = @_;
  157. my $what = $a[1];
  158. my $return;
  159. my $name = $hash->{NAME};
  160. my $address = $hash->{IP_ADDRESS};
  161. $hash->{IP_ADDRESS} = $address;
  162. return "Argument missing." if(int(@a) < 2);
  163. if($what eq "reading")
  164. {
  165. if(exists($hash->{READINGS}{$a[2]}))
  166. {
  167. if(defined($hash->{READINGS}{$a[2]}))
  168. {
  169. return $hash->{READINGS}{$a[2]}{VAL};
  170. }
  171. else
  172. {
  173. return "No such reading: $what";
  174. }
  175. }
  176. }
  177. elsif($what eq "deviceInfo")
  178. {
  179. return "Device info:\n\n".
  180. join("\n", map {sprintf("%-17s: %-17s", $_, $hash->{helper}{dInfo}{$_})} sort keys %{$hash->{helper}{dInfo}});
  181. }
  182. else
  183. {
  184. $return = "Unknown argument $what, choose one of"
  185. ." deviceInfo:noArg"
  186. ." reading:".(join(",",(sort keys %{$hash->{READINGS}})));
  187. return $return;
  188. }
  189. }
  190. sub PHILIPS_AUDIO_Set
  191. {
  192. my ($hash, @a) = @_;
  193. my $name = $hash->{NAME};
  194. my $port = $hash->{PORT};
  195. my $address = $hash->{IP_ADDRESS};
  196. if(not defined($hash->{MODEL}))
  197. {
  198. return "Please provide the model information as argument.";
  199. }
  200. return "No Argument given" if(!defined($a[1]));
  201. my $what = $a[1];
  202. my $usage = "";
  203. my $model = $hash->{MODEL};
  204. $hash->{helper}{dInfo}{MODEL} = $model;
  205. $hash->{helper}{dInfo}{NAME} = $name;
  206. $hash->{helper}{dInfo}{PORT} = $port;
  207. $hash->{helper}{dInfo}{IP_ADDRESS} = $address;
  208. $usage = "Unknown argument $what, choose one of".
  209. " volumeStraight:slider,0,1,64 ".
  210. " volume:slider,0,1,100 ".
  211. " volumeUp:noArg ".
  212. " volumeDown:noArg ".
  213. " standbyButton:noArg ".
  214. " player:next,prev,play-pause,stop ".
  215. " shuffle:on,off ".
  216. " input:---,".
  217. ((uc($model) eq "AW9000") ? "analogAux," : ""). # Input implemented in AW9000 only
  218. ((uc($model) eq "AW9000") ? "digital1Coaxial," : ""). # Input implemented in AW9000 only
  219. ((uc($model) eq "AW9000") ? "digital2Optical," : ""). # Input implemented in AW9000 only
  220. # Input not implemented in the AW9000. Only as DLNA renderer
  221. ((uc($model) ne "AW9000") ? "mediaLibrary," : "").
  222. "internetRadio,onlineServices,mp3Link ". # Available in all devices
  223. #" statusRequest:noArg".
  224. " getPresets:noArg".
  225. " getFavorites:noArg".
  226. " getMediaRendererDesc:noArg".
  227. " favoriteAdd:noArg".
  228. " favoriteRemove:noArg".
  229. " repeat:single,all,off".
  230. #" home:noArg".
  231. " mute:on,off ";
  232. my @favoriteList;
  233. my @favoriteNumber;
  234. foreach my $readings (keys % {$hash->{READINGS}})
  235. {
  236. push @favoriteList,$1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^.inetRadioFavorite_(..)/);
  237. push @favoriteNumber, $1 if($readings =~ m/^.inetRadioFavorite_(..)/);
  238. }
  239. (s/\*/\[asterisk\]/g) for @favoriteList; # '*' not shown correctly
  240. (s/#/\[hash\]/g) for @favoriteList; # '#' not shown correctly
  241. (s/[\\]//g) for @favoriteList;
  242. (s/[ :;,']/_/g) for @favoriteList; # Replace not allowed characters
  243. my @presetList;
  244. my @presetNumber;
  245. foreach my $readings (keys % {$hash->{READINGS}})
  246. {
  247. push @presetList, $1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^.inetRadioPreset_(..)/);
  248. push @presetNumber, $1 if($readings =~ m/^.inetRadioPreset_(..)/);
  249. }
  250. (s/\*/\[asterisk\]/g) for @presetList; # '*' not shown correctly
  251. (s/#/\[hash\]/g) for @presetList; # '#' not shown correctly
  252. (s/[\\]//g) for @presetList; # Replace \
  253. (s/[ :;,']/_/g) for @presetList; # Replace not allowed characters
  254. $usage .= "selectFavorite:" .join(",",("---",(sort @favoriteList))) . " ";
  255. $usage .= "selectPreset:" .join(",",("---",(sort @presetList))) . " ";
  256. $usage .= "selectPresetByNumber:" .join(",",("---",(sort @presetNumber))) . " ";
  257. $usage .= "selectFavoriteByNumber:".join(",",("---",(sort @favoriteNumber))) . " ";
  258. # Direct stream selection if any
  259. my @selectStream;
  260. for(my $lvl = 1; $lvl < int(ReadingsVal($name, ".listDepthLevel", "1") - 1); $lvl++)
  261. {
  262. my $listLevelName = ReadingsVal($name, ".lvl_".$lvl."_name", "");
  263. push @selectStream, "lvl_".$lvl."_".$listLevelName;
  264. }
  265. foreach my $readings (keys % {$hash->{READINGS}})
  266. {
  267. push @selectStream,$1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^listItem_(...)/);
  268. }
  269. @selectStream = sort map{s/\*/\[asterisk\]/g;$_;} grep/._..*$/, @selectStream; # Replace *
  270. @selectStream = sort map{s/#/\[hash\]/g;$_;} grep/._..*$/, @selectStream; # Replace #
  271. @selectStream = sort map{s/[\\]//g;$_;} grep/._..*$/, @selectStream; # Replace not allowed characters
  272. @selectStream = sort map{s/[ :;,']/_/g;$_;} grep/._..*$/, @selectStream; # Replace not allowed characters
  273. $usage .= "selectStream:".join(",",("---",(sort @selectStream))) . " ";
  274. Log3 $name, 5, "PHILIPS_AUDIO ($name) - set ".join(" ", @a);
  275. # External Command. Not from buffer timer.
  276. $hash->{helper}{fromSendCommandBuffer} = 0;
  277. if($what =~ /input|selectStream|selectPreset|selectFavorite/)
  278. {
  279. # GetStatus while manual operation causes the device to stuck. Supress.
  280. # Change device state to "browsing" in order to suppress automatic update
  281. $hash->{helper}{playerState} = "browsing";
  282. $hash->{helper}{manualOperation} = 1;
  283. readingsBeginUpdate($hash);
  284. readingsBulkUpdate($hash, "playerState", "browsing");
  285. readingsBulkUpdate($hash, ".manualOperation", "yes", 1);
  286. readingsEndUpdate($hash, 1);
  287. # Reset browsing timeout
  288. $hash->{helper}{playerBrowsingTimeout} = 0;
  289. }
  290. if($what eq "standbyButton")
  291. {
  292. readingsSingleUpdate($hash, "input", "-", 1);
  293. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STANDBY", "",$what, "noArg");
  294. }
  295. elsif($what eq "getMediaRendererDesc")
  296. {
  297. PHILIPS_AUDIO_getMediaRendererDesc($hash);
  298. }
  299. elsif($what eq "input")
  300. {
  301. if ($a[2] =~ /analogAux|mp3Link|digital1Coaxial|digital2Optical/)
  302. {
  303. # Delete List related readings
  304. delete $hash->{READINGS}{$_} foreach (grep /list/, keys %{$hash->{READINGS}});
  305. # Delete player related readings
  306. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  307. }
  308. if($a[2] eq "analogAux")
  309. {
  310. readingsSingleUpdate($hash, "input", "Aux-in (analog)", 1);
  311. PHILIPS_AUDIO_SendCommand($hash, "/aux", "",$what, $a[2]);
  312. }
  313. elsif($a[2] eq "mp3Link")
  314. {
  315. $hash->{helper}{playerState} = "home";
  316. readingsBeginUpdate($hash);
  317. readingsBulkUpdate($hash, "input", "MP3-Link");
  318. readingsBulkUpdate($hash, "playerState", "home");
  319. readingsEndUpdate($hash, 1);
  320. if(uc($model) eq "AW9000")
  321. {
  322. PHILIPS_AUDIO_SendCommand($hash, "/mp3link", "", $what, $a[2]);
  323. }
  324. else
  325. {
  326. PHILIPS_AUDIO_SendCommand($hash, "/aux", "", $what, $a[2]);
  327. }
  328. }
  329. elsif($a[2] eq "digital1Coaxial")
  330. {
  331. $hash->{helper}{playerState} = "home";
  332. readingsBeginUpdate($hash);
  333. readingsBulkUpdate($hash, "input", "Digital-in 1 (coaxial)");
  334. readingsBulkUpdate($hash, "playerState", "home");
  335. readingsEndUpdate($hash, 1);
  336. PHILIPS_AUDIO_SendCommand($hash, "/digin_coaxial", "",$what, $a[2]);
  337. }
  338. elsif($a[2] eq "digital2Optical")
  339. {
  340. $hash->{helper}{playerState} = "home";
  341. readingsBeginUpdate($hash);
  342. readingsBulkUpdate($hash, "input", "Digital-in 2 (optical)");
  343. readingsBulkUpdate($hash, "playerState", "home");
  344. readingsEndUpdate($hash, 1);
  345. PHILIPS_AUDIO_SendCommand($hash, "/digin_optical", "",$what, $a[2]);
  346. }
  347. elsif($a[2] eq "mediaLibrary")
  348. {
  349. readingsSingleUpdate($hash, "playerListStatus", "busy", 1);
  350. #readingsSingleUpdate($hash, "input", "Media Library", 1);
  351. PHILIPS_AUDIO_SendCommand($hash, "/nav\$02\$01\$001\$0", "", $what, $a[2]);
  352. }
  353. elsif($a[2] eq "internetRadio")
  354. {
  355. readingsSingleUpdate($hash, "playerListStatus", "busy", 1);
  356. #readingsSingleUpdate($hash, "input", "Internet Radio", 1);
  357. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "",$what, $a[2]);
  358. }
  359. elsif($a[2] eq "onlineServices")
  360. {
  361. readingsSingleUpdate($hash, "playerListStatus", "busy", 1);
  362. #readingsSingleUpdate($hash, "input", "Online Services", 1);
  363. PHILIPS_AUDIO_SendCommand($hash, "/nav\$09\$01\$001\$0", "", $what, $a[2]);
  364. }
  365. else
  366. {
  367. return $usage;
  368. }
  369. }
  370. elsif($what eq "home")
  371. {
  372. readingsSingleUpdate($hash, "playerListStatus", "busy", 1);
  373. $hash->{header}{httpHeaderRefer} = "Upgrade-Insecure-Requests: 1\r\n";
  374. PHILIPS_AUDIO_SendCommand($hash, "/index", "",$what, "noArg");
  375. }
  376. elsif($what eq "shuffle")
  377. {
  378. if($a[2] eq "on")
  379. {
  380. readingsSingleUpdate($hash, "playerShuffle", "on", 1);
  381. PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_ON", "",$what, $a[2]);
  382. }
  383. elsif($a[2] eq "off")
  384. {
  385. readingsSingleUpdate($hash, "playerShuffle", "off", 1);
  386. PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_OFF", "",$what, $a[2]);
  387. }
  388. else
  389. {
  390. return $usage;
  391. }
  392. }
  393. elsif($what eq "repeat")
  394. {
  395. if($a[2] eq "single")
  396. {
  397. readingsSingleUpdate($hash, "playerRepeat", "single", 1);
  398. PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_SINGLE", "",$what, $a[2]);
  399. }
  400. elsif($a[2] eq "all")
  401. {
  402. readingsSingleUpdate($hash, "playerRepeat", "all", 1);
  403. PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_ALL", "",$what, $a[2]);
  404. }
  405. elsif($a[2] eq "off")
  406. {
  407. readingsSingleUpdate($hash, "playerRepeat", "off", 1);
  408. PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_OFF", "",$what, $a[2]);
  409. }
  410. else
  411. {
  412. return $usage;
  413. }
  414. }
  415. elsif($what eq "statusRequest")
  416. {
  417. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg");
  418. }
  419. elsif($what eq "favoriteAdd")
  420. {
  421. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$ADD2FAV", "",$what, "noArg");
  422. }
  423. elsif($what eq "favoriteRemove")
  424. {
  425. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$REMFAV", "",$what, "noArg");
  426. }
  427. elsif($what eq "mute")
  428. {
  429. if($a[2] eq "on")
  430. {
  431. readingsSingleUpdate($hash, "mute", "on", 1);
  432. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$MUTE", "",$what, "noArg");
  433. }
  434. elsif($a[2] eq "off")
  435. {
  436. readingsSingleUpdate($hash, "mute", "off", 1);
  437. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$UNMUTE", "",$what, "noArg");
  438. }
  439. else
  440. {
  441. return $usage;
  442. }
  443. }
  444. elsif($what eq "player")
  445. {
  446. if($a[2] eq "next")
  447. {
  448. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$NEXT", "",$what, "noArg");
  449. }
  450. elsif($a[2] eq "prev")
  451. {
  452. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PREV", "",$what, "noArg");
  453. }
  454. elsif($a[2] eq "play-pause")
  455. {
  456. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PLAY_PAUSE", "",$what, "noArg");
  457. }
  458. elsif($a[2] eq "stop")
  459. {
  460. PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STOP", "",$what, "noArg");
  461. }
  462. else
  463. {
  464. return $usage;
  465. }
  466. }
  467. elsif($what =~ m/^(selectPreset|selectPresetByNumber)/)
  468. {
  469. if($a[2] ne "---")
  470. {
  471. # Hierarchichal navigation through the contents mandatory
  472. $hash->{helper}{cmdStep} = 1;
  473. my $presetNumber = substr($a[2], 0, 2); # Get 2 first digits
  474. if($a[2] =~ m/empty/ or ReadingsVal($name, ".inetRadioPreset_$presetNumber","") eq "empty")
  475. {
  476. # Do nothing
  477. }
  478. else
  479. {
  480. $presetNumber =~ s/^0+//g; # Remove leading '0'
  481. $hash->{helper}{inetRadioPreset} = $presetNumber;
  482. readingsSingleUpdate($hash, "input", "Internet Radio", 1);
  483. PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]);
  484. }
  485. }
  486. }
  487. elsif($what =~ m/^(selectFavorite|selectFavoriteByNumber)/)
  488. {
  489. if($a[2] ne "---")
  490. {
  491. # Hierarchichal navigation through the contents mandatory
  492. $hash->{helper}{cmdStep} = 1;
  493. my $favoriteNumber = substr($a[2], 0, 2); # Get 2 first digits
  494. if($a[2] =~ m/empty/ or ReadingsVal($name, ".inetRadioFavorite_$favoriteNumber","") eq "empty")
  495. {
  496. # Do nothing
  497. }
  498. else
  499. {
  500. $favoriteNumber =~ s/^0+//g; # Remove leading '0'
  501. $hash->{helper}{inetRadioFavorite} = $favoriteNumber;
  502. #readingsSingleUpdate($hash, "input", "Internet Radio", 1);
  503. PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]);
  504. }
  505. }
  506. }
  507. elsif($what eq "volumeStraight")
  508. {
  509. if(($a[2] < 0) || ($a[2] > 64))
  510. {
  511. return "volumeStraight must be in the range 0...64.";
  512. }
  513. else
  514. {
  515. $hash->{helper}{targetVolume} = int($a[2]);
  516. readingsBeginUpdate($hash);
  517. readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume});
  518. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}));
  519. readingsEndUpdate($hash, 1);
  520. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]);
  521. }
  522. }
  523. elsif($what eq "volumeUp")
  524. {
  525. my $targetVolume = int(int(ReadingsVal($name, "volumeStraight", "0")) + 1);
  526. if($targetVolume > 64)
  527. {
  528. $targetVolume = 64;
  529. }
  530. $hash->{helper}{targetVolume} = $targetVolume;
  531. readingsBeginUpdate($hash);
  532. readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume});
  533. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}));
  534. readingsEndUpdate($hash, 1);
  535. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$" . $hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume});
  536. }
  537. elsif($what eq "volumeDown")
  538. {
  539. my $targetVolume = int(int(ReadingsVal($name, "volumeStraight", "0")) - 1);
  540. if($targetVolume < 0)
  541. {
  542. $targetVolume = 0;
  543. }
  544. $hash->{helper}{targetVolume} = $targetVolume;
  545. readingsBeginUpdate($hash);
  546. readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume});
  547. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}));
  548. readingsEndUpdate($hash, 1);
  549. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$" . $hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume});
  550. }
  551. elsif($what eq "volume")
  552. {
  553. if(($a[2] < 0) || ($a[2] > 100))
  554. {
  555. return "volumeStraight must be in the range 0...100.";
  556. }
  557. else
  558. {
  559. $hash->{helper}{targetVolume} = PHILIPS_AUDIO_volume_rel2abs($hash, $a[2]);
  560. readingsBeginUpdate($hash);
  561. readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume});
  562. readingsBulkUpdate($hash, "volume", $a[2]);
  563. readingsEndUpdate($hash, 1);
  564. PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume});
  565. }
  566. }
  567. elsif($what eq "nowplay")
  568. {
  569. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "",$what, "noArg");
  570. }
  571. elsif($what eq "homestatus")
  572. {
  573. $hash->{helper}{httpHeaderRefer} = "http://$hash->{IP_ADDRESS}:$hash->{PORT}/index\r\n";
  574. PHILIPS_AUDIO_SendCommand($hash, "/HOMESTATUS", "",$what, "noArg");
  575. }
  576. elsif($what eq "getPresets")
  577. {
  578. readingsBeginUpdate($hash);
  579. readingsBulkUpdate($hash, "playerListStatus", "busy");
  580. readingsBulkUpdate($hash, "readingPresets", "yes");
  581. readingsEndUpdate($hash, 1);
  582. # Delete old redings
  583. delete $hash->{READINGS}{$_} foreach (grep /.inetRadioPreset_..$/, keys %{$hash->{READINGS}});
  584. # Hierarchichal navigation through the contents mandatory
  585. $hash->{helper}{cmdStep} = 1;
  586. PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, "noArg");
  587. }
  588. elsif($what eq "getFavorites")
  589. {
  590. readingsBeginUpdate($hash);
  591. readingsBulkUpdate($hash, "playerListStatus", "busy");
  592. readingsBulkUpdate($hash, "readingFavorites", "yes");
  593. readingsEndUpdate($hash, 1);
  594. # Delete old redings
  595. delete $hash->{READINGS}{$_} foreach (grep /.inetRadioFavorite_..$/, keys %{$hash->{READINGS}});
  596. # Hierarchichal navigation through the contents mandatory
  597. $hash->{helper}{cmdStep} = 1;
  598. PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, "noArg");
  599. }
  600. elsif($what eq "selectStream")
  601. {
  602. # The player list selection has been designed for a GUI/touchscreen
  603. # Virtually scrolling down to last item and choosing the first one
  604. # arises an error. Needs to navigate back to the corresponding page
  605. # consisting of 8 items per page
  606. readingsSingleUpdate($hash, "playerListStatus", "busy", 1);
  607. if($a[2] =~ /lvl_(.+?)/)
  608. {
  609. my $desiredLevel = $1;
  610. my $currentUrl = $hash->{helper}{currentNavUrl};
  611. my $newUrl = "";
  612. my $currentLevel = "";
  613. my $newLevel = "";
  614. if($currentUrl =~ /\/nav\$(.*)\$(.*)\$(.*)\$(.*)/) # e.g. /nav$02$XX$001$0
  615. {
  616. $currentLevel = int($2);
  617. $newLevel = sprintf("%02d", $currentLevel - ($currentLevel - $desiredLevel) + 1);
  618. $newUrl = "/nav\$$1\$$newLevel\$$3\$0";
  619. }
  620. PHILIPS_AUDIO_SendCommand($hash, $newUrl, "", $what, $a[2]);
  621. }
  622. elsif($a[2] ne "---")
  623. {
  624. my $targetNr = substr($a[2], 0, 3);
  625. my $currentUrl = $hash->{helper}{currentNavUrl};
  626. # Remark TODO...
  627. if($a[2] =~ /(\d{3})_(.+?)_(.*)/)
  628. {
  629. my $currentListLevel = ReadingsVal($name, ".listDepthLevel", "");
  630. readingsSingleUpdate($hash, ".lvl_".$currentListLevel."_name", "$2_$3", 1);
  631. if(int(ReadingsVal($name, "playerListTotalCount", "8")) > 8)
  632. {
  633. if($currentUrl =~ /\/nav\$(.*)\$(.*)\$(.*)\$(.*)/)
  634. {
  635. # Virtually scroll back to the first line
  636. my $scrollUrl = "\/nav\$$1\$$2\$001\$$4";
  637. PHILIPS_AUDIO_SendCommand($hash, $scrollUrl, "", "selectStream", "scroll");
  638. # int(($targetNr / 8) + 0.5) -> Round up
  639. for(my $i = 1; $i < (int(($targetNr / 8) + 0.5) + 1); $i += 8) # Max 8 items in the actual list
  640. {
  641. my $scrollUrl = "\/nav\$$1\$$2\$".sprintf("%003s", ($i + 8))."\$$4";
  642. PHILIPS_AUDIO_SendCommand($hash, $scrollUrl, "", "selectStream", "scroll");
  643. }
  644. }
  645. }
  646. }
  647. PHILIPS_AUDIO_SendCommand($hash, ReadingsVal($name, ".listItemTarget_$targetNr", ""), "", $what, $a[2]);
  648. }
  649. else
  650. {
  651. return $usage;
  652. }
  653. }
  654. else
  655. {
  656. return $usage;
  657. }
  658. PHILIPS_AUDIO_ResetTimer($hash); # Reset timer for the next loop
  659. return;
  660. }
  661. sub PHILIPS_AUDIO_Define
  662. {
  663. my ($hash, $def) = @_;
  664. my @a = split("[ \t][ \t]*", $def);
  665. my $name = $hash->{NAME};
  666. # Implemented devices for reading Device Description
  667. @{$hash->{helper}{DevDescImplementedModels}} = qw( AW9000 NP3500 NP3700 NP3900 );
  668. $hash->{helper}{sendCommandBuffer} = [];
  669. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  670. if(! @a >= 4)
  671. {
  672. my $msg = "Wrong syntax: define <name> PHILIPS_AUDIO <model> <ip-or-hostname> [<ON-statusinterval>] [<OFF-statusinterval>] ";
  673. Log3 $name, 2, $msg;
  674. return $msg;
  675. }
  676. if(defined($a[2]))
  677. {
  678. $hash->{MODEL} = uc($a[2]);
  679. # Used by 'fheminfo' command for statistics
  680. $attr{$name}{"model"} = $hash->{MODEL};
  681. }
  682. $hash->{IP_ADDRESS} = $a[3];
  683. $hash->{PORT} = 8889;
  684. # if an update interval >= 5 use it.
  685. if(defined($a[4]) and $a[4] > 0)
  686. {
  687. $hash->{helper}{OFF_INTERVAL} = $a[4];
  688. # Minimum interval 5 sec
  689. if($hash->{helper}{OFF_INTERVAL} < 5)
  690. {
  691. $hash->{helper}{OFF_INTERVAL} = 5;
  692. }
  693. }
  694. else
  695. {
  696. $hash->{helper}{OFF_INTERVAL} = 30;
  697. }
  698. if(defined($a[5]) and $a[5] > 0)
  699. {
  700. $hash->{helper}{ON_INTERVAL} = $a[5];
  701. # Minimum interval 5 sec
  702. if($hash->{helper}{ON_INTERVAL} < 5)
  703. {
  704. $hash->{helper}{ON_INTERVAL} = 5;
  705. }
  706. }
  707. else
  708. {
  709. $hash->{helper}{ON_INTERVAL} = $hash->{helper}{OFF_INTERVAL};
  710. }
  711. unless(exists($hash->{helper}{AVAILABLE}) and ($hash->{helper}{AVAILABLE} == 0))
  712. {
  713. $hash->{helper}{AVAILABLE} = 1;
  714. readingsSingleUpdate($hash, "presence", "present", 1);
  715. }
  716. # start the status update timer
  717. $hash->{helper}{DISABLED} = 0 unless(exists($hash->{helper}{DISABLED}));
  718. PHILIPS_AUDIO_ResetTimer($hash, 0);
  719. return;
  720. }
  721. sub PHILIPS_AUDIO_Attr
  722. {
  723. my ($cmd, $name, $attrName, $attrVal) = @_;
  724. my $hash = $defs{$name};
  725. if($attrName eq "disable")
  726. {
  727. if ($cmd eq "set")
  728. {
  729. $hash->{helper}{DISABLED} = $attrVal;
  730. if ($attrVal eq "0")
  731. {
  732. PHILIPS_AUDIO_GetStatus($hash, 1);
  733. }
  734. }
  735. else
  736. {
  737. $hash->{helper}{DISABLED} = 0;
  738. PHILIPS_AUDIO_GetStatus($hash, 1);
  739. }
  740. }
  741. elsif($attrName eq "maxListItems" && defined($attrVal))
  742. {
  743. if ($cmd eq "set" && (($attrVal < 8) || ($attrVal > 999)))
  744. {
  745. return "$attrName must be between 8 and 999";
  746. }
  747. }
  748. elsif($attrName eq "httpBufferTimeout" && defined($attrVal))
  749. {
  750. if ($cmd eq "set" && (($attrVal < 10) || ($attrVal > 15)))
  751. {
  752. return "$attrName must be between 10 and 15";
  753. }
  754. }
  755. elsif($attrName eq "requestTimeout" && defined($attrVal))
  756. {
  757. if ($cmd eq "set" && (($attrVal < 10) || ($attrVal > 15)))
  758. {
  759. return "$attrName must be between 10 and 15";
  760. }
  761. }
  762. elsif($attrName eq "autoGetPresets" && defined($attrVal))
  763. {
  764. if ($cmd eq "set" && (($attrVal < 0) || ($attrVal > 1)))
  765. {
  766. return "$attrName must be between 0 or 1";
  767. }
  768. }
  769. elsif($attrName eq "autoGetFavorites" && defined($attrVal))
  770. {
  771. if ($cmd eq "set" && (($attrVal < 0) || ($attrVal > 1)))
  772. {
  773. return "$attrName must be between 0 or 1";
  774. }
  775. }
  776. elsif($attrName eq "playerBrowsingTimeout" && defined($attrVal))
  777. {
  778. if ($cmd eq "set" && (($attrVal < 60) || ($attrVal > 600)))
  779. {
  780. return "$attrName must be between 60 or 600";
  781. }
  782. }
  783. # Start/Stop Timer according to new disabled-Value
  784. PHILIPS_AUDIO_ResetTimer($hash);
  785. return;
  786. }
  787. sub PHILIPS_AUDIO_Undefine
  788. {
  789. my($hash, $name) = @_;
  790. # Stop the internal GetStatus-Loop and exit
  791. RemoveInternalTimer($hash, "PHILIPS_AUDIO_GetStatus");
  792. return;
  793. }
  794. sub PHILIPS_AUDIO_SendCommandBuffer
  795. {
  796. # Function called by an internal timer in case device busy
  797. my ($hash) = @_;
  798. my $firstCommand = "";
  799. my ($url, $data, $cmd, $arg) = ("", "", "", "");
  800. # Get first command from buffer
  801. if
  802. (
  803. @{$hash->{helper}{sendCommandBuffer}}
  804. )
  805. {
  806. $firstCommand = shift(@{$hash->{helper}{sendCommandBuffer}});
  807. $url = $firstCommand->{url};
  808. $data = $firstCommand->{data};
  809. $cmd = $firstCommand->{cmd};
  810. $arg = $firstCommand->{arg};
  811. }
  812. # Only send in case buffer not empty
  813. if(($url ne "") and ($cmd ne "") and ($arg ne "")) # $data may be empty
  814. {
  815. $hash->{helper}{comeFromSendBuffer} = 1;
  816. PHILIPS_AUDIO_SendCommand(
  817. $hash,
  818. $url,
  819. $data,
  820. $cmd,
  821. $arg
  822. );
  823. # Check if buffer empty
  824. if
  825. (
  826. (@{$hash->{helper}{sendCommandBuffer}})
  827. )
  828. {
  829. # Come back
  830. # -> try again after 1 sec delay and process buffer.
  831. RemoveInternalTimer($hash, "PHILIPS_AUDIO_SendCommandBuffer");
  832. InternalTimer(gettimeofday() + 1, "PHILIPS_AUDIO_SendCommandBuffer", $hash);
  833. }
  834. }
  835. else
  836. {
  837. # Do nothing.
  838. # Reset flag in case buffer empty
  839. $hash->{helper}{comeFromSendBuffer} = 0;
  840. PHILIPS_AUDIO_ResetTimer($hash);
  841. }
  842. return;
  843. }
  844. sub PHILIPS_AUDIO_SendCommand
  845. {
  846. my ($hash, $url, $data, $cmd, $arg) = @_;
  847. my $name = $hash->{NAME};
  848. my $address = $hash->{IP_ADDRESS};
  849. my $port = $hash->{PORT};
  850. $hash->{helper}{networkRequest} = "idle" if(not defined($hash->{helper}{networkRequest})); # First run
  851. $hash->{helper}{comeFromSendBuffer} = 0 if(not defined($hash->{helper}{comeFromSendBuffer})); # First run
  852. # buffer command and wait a second in case device busy
  853. if ($hash->{helper}{networkRequest} eq "busy")
  854. {
  855. my $httpBufferData =
  856. {
  857. url => $url,
  858. data => $data,
  859. cmd => $cmd,
  860. arg => $arg
  861. };
  862. # Append to buffer only if not coming from itself
  863. # Or put to first position for resending
  864. # add remove start end
  865. # push X X
  866. # pop X X
  867. # unshift X X
  868. # shift X X
  869. if($hash->{helper}{comeFromSendBuffer} == 0)
  870. {
  871. push @{$hash->{helper}{sendCommandBuffer}}, $httpBufferData;
  872. }
  873. else
  874. {
  875. unshift @{$hash->{helper}{sendCommandBuffer}}, $httpBufferData;
  876. }
  877. if($hash->{helper}{timeoutCounter} >= AttrVal($name, "httpBufferTimeout", 10))
  878. {
  879. # X seconds timeout. Something went wrong. Clear buffer. Release "busy"
  880. # Delete all remaining commands
  881. splice(@{$hash->{helper}{sendCommandBuffer}});
  882. $hash->{helper}{timeoutCounter} = 0;
  883. $hash->{helper}{networkRequest} = "idle";
  884. $hash->{helper}{comeFromSendBuffer} = 0;
  885. readingsBeginUpdate($hash);
  886. readingsBulkUpdate ($hash, "networkRequest", "idle");
  887. readingsBulkUpdate ($hash, "networkError", "buffer timed out");
  888. readingsEndUpdate ($hash, 1);
  889. Log3 $name, 3, "PHILIPS_AUDIO ($name) - HTTP buffer timeout. Please, check your network connection, ip-address etc. Device switched off?";
  890. PHILIPS_AUDIO_ResetTimer($hash);
  891. return;
  892. }
  893. # -> try again after 1 sec delay and process buffer.
  894. RemoveInternalTimer($hash, "PHILIPS_AUDIO_SendCommandBuffer");
  895. InternalTimer(gettimeofday() + 1, "PHILIPS_AUDIO_SendCommandBuffer", $hash);
  896. return;
  897. }
  898. else
  899. {
  900. if($url =~ /\/nav(.*)/)
  901. {
  902. $hash->{helper}{currentNavUrl} = $url;
  903. }
  904. else
  905. {
  906. $hash->{helper}{currentUrl} = $url;
  907. }
  908. if(ReadingsVal($name, "presence", "absent") ne "absent")
  909. {
  910. $hash->{helper}{networkRequest} = "busy";
  911. readingsSingleUpdate($hash, "networkRequest", "busy", 1);
  912. }
  913. PHILIPS_AUDIO_ResetTimer($hash); # getStatus
  914. }
  915. $hash->{helper}{timeoutCounter} = 0;
  916. Log3 $name, 5, "PHILIPS_AUDIO ($name) - Executing nonblocking \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" on $name: $data";
  917. # Reset flag if successfully sent
  918. $hash->{helper}{comeFromSendBuffer} = 0;
  919. HttpUtils_NonblockingGet
  920. ({
  921. url => "http://".$address.":".$port."".$url,
  922. timeout => AttrVal($name, "requestTimeout", 10),
  923. noshutdown => 1,
  924. data => $data,
  925. loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
  926. hash => $hash,
  927. cmd => $cmd,
  928. arg => $arg,
  929. method => "GET",
  930. httpversion => "1.1",
  931. keepalive => 1, # Philips app always uses keep-alive
  932. header => $hash->{helper}{httpHeaderRefer},
  933. callback => \&PHILIPS_AUDIO_ParseResponse
  934. });
  935. return;
  936. }
  937. sub PHILIPS_AUDIO_ParseResponse
  938. {
  939. my ($param, $err, $data ) = @_;
  940. my $hash = $param->{hash};
  941. my $name = $hash ->{NAME};
  942. my $cmd = $param->{cmd};
  943. my $arg = $param->{arg};
  944. if(exists($param->{code}))
  945. {
  946. Log3 $name, 5, "PHILIPS_AUDIO ($name) - received HTTP code ".$param->{code}." for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\"";
  947. }
  948. if($err ne "")
  949. {
  950. readingsBeginUpdate($hash);
  951. Log3 $name, 5, "PHILIPS_AUDIO ($name) - Could not execute command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $err";
  952. # Release the busy flag
  953. $hash->{helper}{networkRequest} = "idle";
  954. readingsBulkUpdate($hash, "networkRequest", "idle");
  955. if((not exists($hash->{helper}{AVAILABLE})) or (exists($hash->{helper}{AVAILABLE}) and $hash->{helper}{AVAILABLE} == 1))
  956. {
  957. Log3 $name, 3, "PHILIPS_AUDIO ($name) - Could not execute HTTP request. Please, check your network connection, ip-address etc. Device switched off? ($err)";
  958. readingsBulkUpdate($hash, "networkError", "$err");
  959. readingsBulkUpdate($hash, "presence", "absent");
  960. readingsBulkUpdate($hash, "state", "absent");
  961. $hash->{STATE} = "absent";
  962. }
  963. $hash->{helper}{AVAILABLE} = 0;
  964. $hash->{helper}{timeoutCounter} = 0;
  965. $hash->{helper}{comeFromSendBuffer} = 0;
  966. # Close HTTP connection
  967. HttpUtils_Close($param);
  968. # Force "home" state
  969. $hash->{helper}{playerState} = "home";
  970. readingsBulkUpdate($hash, "playerState", "home");
  971. readingsBulkUpdate($hash, "playerListStatus", "ready");
  972. readingsEndUpdate($hash, 1);
  973. # Device firware "somehow buggy".
  974. # Try to reanimate the device after timeout.
  975. # Go to /index
  976. PHILIPS_AUDIO_SendCommand($hash, "/index", "","home", "noArg");
  977. PHILIPS_AUDIO_ResetTimer($hash);
  978. return;
  979. }
  980. elsif($data ne "")
  981. {
  982. Log3 $name, 5, "PHILIPS_AUDIO ($name) - got response for \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $data";
  983. delete $hash->{READINGS}{networkError};
  984. readingsBeginUpdate($hash);
  985. if (defined($hash->{helper}{AVAILABLE}) and $hash->{helper}{AVAILABLE} eq 0)
  986. {
  987. Log3 $name, 3, "PHILIPS_AUDIO ($name) - device $name reappeared";
  988. readingsBulkUpdate($hash, "presence", "present");
  989. }
  990. $hash->{helper}{AVAILABLE} = 1;
  991. readingsBulkUpdate($hash, "power", "on");
  992. readingsBulkUpdate($hash, "state","on");
  993. $hash->{STATE} = "on";
  994. readingsEndUpdate($hash, 1);
  995. if($cmd eq "standbyButton")
  996. {
  997. if($data =~ /SUCCESS/)
  998. {
  999. #readingsBulkUpdate($hash, "power", "on");
  1000. #readingsBulkUpdate($hash, "state","on");
  1001. }
  1002. }
  1003. elsif($cmd eq "home")
  1004. {
  1005. if($data =~ /'devicename'/) # Device responded correctly
  1006. {
  1007. $hash->{helper}{playerState} = "home";
  1008. readingsBeginUpdate($hash);
  1009. readingsBulkUpdate ($hash, "playerState", "home");
  1010. readingsBulkUpdate ($hash, "playerListStatus", "ready");
  1011. readingsEndUpdate ($hash, 1);
  1012. }
  1013. }
  1014. elsif($cmd eq "mute")
  1015. {
  1016. if($data =~ /SUCCESS/)
  1017. {
  1018. readingsSingleUpdate($hash, "mute", "on", 1);
  1019. }
  1020. }
  1021. elsif($cmd eq "unmute")
  1022. {
  1023. if($data =~ /SUCCESS/)
  1024. {
  1025. readingsSingleUpdate($hash, "mute", "off", 1);
  1026. }
  1027. }
  1028. elsif($cmd eq "favoriteRemove")
  1029. {
  1030. # replace \n by ""
  1031. $data =~ s/\n//g;
  1032. if($data =~ /{'command':'MESSAGE','value':'(.+)'}/)
  1033. {
  1034. # Do nothing
  1035. }
  1036. }
  1037. elsif($cmd eq "favoriteAdd")
  1038. {
  1039. # replace \n by ""
  1040. $data =~ s/\n//g;
  1041. if($data =~ /{'command':'MESSAGE','value':'(.+)'}/)
  1042. {
  1043. # Do nothing
  1044. }
  1045. }
  1046. elsif($cmd =~ m/^(selectPreset|inetRadioPreset)/)
  1047. {
  1048. # This command must be processed hierarchicaly through the navigation path
  1049. if($hash->{helper}{cmdStep} == 1)
  1050. {
  1051. $hash->{helper}{cmdStep} = 2;
  1052. # External Command. Not from buffer timer.
  1053. $hash->{helper}{fromSendCommandBuffer} = 0;
  1054. # Internet radio
  1055. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "selectPreset", $hash->{helper}{inetRadioPreset});
  1056. }
  1057. elsif($hash->{helper}{cmdStep} == 2)
  1058. {
  1059. $hash->{helper}{cmdStep} = 3;
  1060. # External Command. Not from buffer timer.
  1061. $hash->{helper}{fromSendCommandBuffer} = 0;
  1062. # Presets
  1063. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","selectPreset", $hash->{helper}{inetRadioPreset});
  1064. }
  1065. elsif($hash->{helper}{cmdStep} == 3)
  1066. {
  1067. $hash->{helper}{cmdStep} = 4;
  1068. # External Command. Not from buffer timer.
  1069. $hash->{helper}{fromSendCommandBuffer} = 0;
  1070. # Preset select
  1071. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioPreset})."\$1", "","selectPreset", $hash->{helper}{inetRadioPreset});
  1072. $hash->{helper}{playerState} = "playing";
  1073. readingsSingleUpdate($hash, "playerState", "playing", 1);
  1074. }
  1075. }
  1076. elsif($cmd =~ m/^(selectFavorite|inetRadioFavorite)/)
  1077. {
  1078. # This command must be processed hierarchicaly through the navigation path
  1079. if($hash->{helper}{cmdStep} == 1)
  1080. {
  1081. $hash->{helper}{cmdStep} = 2;
  1082. # Internet radio favorite# External Command. Not from buffer timer.
  1083. $hash->{helper}{fromSendCommandBuffer} = 0;
  1084. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "selectFavorite", $hash->{helper}{inetRadioFavorite});
  1085. }
  1086. elsif($hash->{helper}{cmdStep} == 2)
  1087. {
  1088. $hash->{helper}{cmdStep} = 3;
  1089. # External Command. Not from buffer timer.
  1090. $hash->{helper}{fromSendCommandBuffer} = 0;
  1091. # Favorite Presets
  1092. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","selectFavorite", $hash->{helper}{inetRadioFavorite});
  1093. }
  1094. elsif($hash->{helper}{cmdStep} == 3)
  1095. {
  1096. $hash->{helper}{cmdStep} = 4;
  1097. # External Command. Not from buffer timer.
  1098. $hash->{helper}{fromSendCommandBuffer} = 0;
  1099. # Favorite Preset select
  1100. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioFavorite})."\$1", "","selectFavorite", $hash->{helper}{inetRadioFavorite});
  1101. $hash->{helper}{playerState} = "playing";
  1102. readingsSingleUpdate($hash, "playerState", "playing", 1);
  1103. }
  1104. }
  1105. elsif($cmd eq "play_pause")
  1106. {
  1107. if($data =~ /SUCCESS/)
  1108. {
  1109. if(ReadingsVal($name, "playerPlaying", "") eq "no")
  1110. {
  1111. readingsSingleUpdate($hash, "playerPlaying", "yes", 1);
  1112. }
  1113. else
  1114. {
  1115. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1116. readingsBeginUpdate($hash);
  1117. readingsBulkUpdate($hash, "playerPlaying", "no");
  1118. readingsBulkUpdate($hash, "input", "-");
  1119. readingsEndUpdate($hash, 1);
  1120. }
  1121. }
  1122. }
  1123. elsif($cmd eq "stop")
  1124. {
  1125. if($data =~ /STOP/)
  1126. {
  1127. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1128. readingsBeginUpdate($hash);
  1129. readingsBulkUpdate($hash, "playerPlaying", "no");
  1130. readingsBulkUpdate($hash, "input", "-");
  1131. readingsEndUpdate($hash, 1);
  1132. }
  1133. }
  1134. elsif($cmd =~ m/^(volumeStraight|volumeUp|volumeDown)/)
  1135. {
  1136. if($data =~ /SUCCESS/)
  1137. {
  1138. my $targetVolume = $hash->{helper}{targetVolume};
  1139. readingsBeginUpdate($hash);
  1140. readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume});
  1141. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $targetVolume));
  1142. readingsEndUpdate($hash, 1);
  1143. }
  1144. }
  1145. elsif($cmd eq "elapse")
  1146. {
  1147. if($data =~ /'command':'(.+)',/)
  1148. {
  1149. if($1 eq "NOWPLAY")
  1150. {
  1151. # New player status information available
  1152. readingsSingleUpdate($hash, "playerPlaying", "yes", 1);
  1153. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg");
  1154. }
  1155. elsif($1 eq "ELAPSE")
  1156. {
  1157. readingsBeginUpdate($hash);
  1158. readingsBulkUpdate($hash, "playerPlaying", "yes");
  1159. if($data =~ /'value':(.+),/)
  1160. {
  1161. # Sometimes the device does not refresh the ELAPSE -> NOWPLAY request
  1162. # Showing the current stream position.
  1163. # Check for a new song.
  1164. $hash->{helper}{elapseValueOld} = "0" if(not defined($hash->{helper}{elapseValueOld}));
  1165. if (int($1) < int($hash->{helper}{elapseValueOld})) # New song
  1166. {
  1167. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg");
  1168. }
  1169. $hash->{helper}{elapseValueOld} = $1;
  1170. if(int($1) < 3600)
  1171. {
  1172. readingsBulkUpdate($hash, "playerPlayTime", strftime("%M:\%S", gmtime($1)));
  1173. }
  1174. else
  1175. {
  1176. readingsBulkUpdate($hash, "playerPlayTime", strftime("\%H:\%M:\%S", gmtime($1)));
  1177. }
  1178. }
  1179. if($data =~ /'mute':(.+),/)
  1180. {
  1181. if(int($1) == 1)
  1182. {
  1183. readingsBulkUpdate($hash, "mute", "on");
  1184. }
  1185. else
  1186. {
  1187. readingsBulkUpdate($hash, "mute", "off");
  1188. }
  1189. }
  1190. if($data =~ /'shuffle':(.+),/)
  1191. {
  1192. if(int($1) == 1)
  1193. {
  1194. readingsBulkUpdate($hash, "playerShuffle", "on");
  1195. }
  1196. else
  1197. {
  1198. readingsBulkUpdate($hash, "playerShuffle", "off");
  1199. }
  1200. }
  1201. if($data =~ /'repeat':(.+),/)
  1202. {
  1203. if(int($1) == 0)
  1204. {
  1205. readingsBulkUpdate($hash, "playerRepeat", "off");
  1206. }
  1207. elsif(int($1) == 1)
  1208. {
  1209. readingsBulkUpdate($hash, "playerRepeat", "single");
  1210. }
  1211. elsif(int($1) == 2)
  1212. {
  1213. readingsBulkUpdate($hash, "playerRepeat", "all");
  1214. }
  1215. }
  1216. if($data =~ /'rating':(.+),/)
  1217. {
  1218. readingsBulkUpdate($hash, "playerStreamRating", $1);
  1219. }
  1220. if($data =~ /'favstatus':(.+)}/)
  1221. {
  1222. if(int($1) == 1)
  1223. {
  1224. readingsBulkUpdate($hash, "playerStreamFavorite", "yes");
  1225. }
  1226. else
  1227. {
  1228. readingsBulkUpdate($hash, "playerStreamFavorite", "no");
  1229. }
  1230. }
  1231. if($data =~ /'volume':(.+),/)
  1232. {
  1233. readingsBulkUpdate($hash, "volumeStraight", $1);
  1234. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $1));
  1235. }
  1236. readingsEndUpdate($hash, 1);
  1237. if($data =~ /'play':(.+),/)
  1238. {
  1239. if(int($1) == 1)
  1240. {
  1241. $hash->{helper}{playerState} = "playing";
  1242. readingsBeginUpdate($hash);
  1243. readingsBulkUpdate ($hash, "playerPlaying", "yes");
  1244. readingsBulkUpdate ($hash, "playerState", "playing");
  1245. readingsEndUpdate ($hash, 1);
  1246. }
  1247. else
  1248. {
  1249. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1250. readingsBeginUpdate($hash);
  1251. readingsBulkUpdate ($hash, "playerPlaying", "no");
  1252. readingsBulkUpdate ($hash, "playerState", "home");
  1253. readingsEndUpdate ($hash, 1);
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. elsif($cmd eq "nowplay")
  1260. {
  1261. if($data =~ /'command':'(.+)',/)
  1262. {
  1263. if($1 eq "NOTHING")
  1264. {
  1265. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1266. $hash->{helper}{playerState} = "home";
  1267. readingsBeginUpdate($hash);
  1268. readingsBulkUpdate ($hash, "playerPlaying", "no");
  1269. readingsBulkUpdate ($hash, "playerState", "home");
  1270. readingsEndUpdate ($hash, 1);
  1271. }
  1272. }
  1273. elsif($data =~ /window.location = \"\/index\"/)
  1274. {
  1275. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1276. $hash->{helper}{playerState} = "home";
  1277. readingsBeginUpdate($hash);
  1278. readingsBulkUpdate ($hash, "playerPlaying", "no");
  1279. readingsBulkUpdate ($hash, "playerState", "home");
  1280. readingsEndUpdate ($hash, 1);
  1281. }
  1282. else
  1283. {
  1284. $hash->{helper}{playerState} = "playing";
  1285. readingsBeginUpdate($hash);
  1286. readingsBulkUpdate ($hash, "playerPlaying", "yes");
  1287. readingsBulkUpdate ($hash, "playerState", "playing");
  1288. readingsEndUpdate ($hash, 1);
  1289. }
  1290. if($data =~ /'devicename':'(.*?)'/)
  1291. {
  1292. if(not defined ($hash->{FRIENDLY_NAME}))
  1293. {
  1294. $hash->{FRIENDLY_NAME} = $1;
  1295. $hash->{helper}{dInfo}{FRIENDLY_NAME} = $1;
  1296. }
  1297. }
  1298. readingsBeginUpdate($hash);
  1299. if ($data =~ /'defaultAlbumArt':'res\/Internet_radio.jpg'/)
  1300. {
  1301. readingsBulkUpdate($hash, "input", "Internet Radio");
  1302. }
  1303. if($data =~ /'defaultAlbumArt':'res\/Media_library.jpg'/)
  1304. {
  1305. readingsBulkUpdate($hash, "input", "Media Library");
  1306. }
  1307. if($data =~ /'defaultAlbumArt':'res\/Home_AUX_nowplaying.jpg'/)
  1308. {
  1309. if($hash->{MODEL} eq "AW9000")
  1310. {
  1311. if($data =~ />Aux-in</)
  1312. {
  1313. readingsBulkUpdate($hash, "input", "Aux-in (analog)");
  1314. }
  1315. elsif($data =~ />Digital-in 1(.+)</)
  1316. {
  1317. readingsBulkUpdate($hash, "input", "Digital-in 1 (coaxial)");
  1318. }
  1319. elsif($data =~ />Digital-in 2(.+)</)
  1320. {
  1321. readingsBulkUpdate($hash, "input", "Digital-in 2 (optical)");
  1322. }
  1323. elsif($data =~ />MP3-Link</)
  1324. {
  1325. readingsBulkUpdate($hash, "input", "MP3-Link");
  1326. }
  1327. }
  1328. else
  1329. {
  1330. readingsBulkUpdate($hash, "input", "MP3-Link");
  1331. }
  1332. }
  1333. if($data =~ /'defaultAlbumArt':'res\/spot_Nowplaying_AA.png'/)
  1334. {
  1335. readingsBulkUpdate($hash, "input", "Spotify");
  1336. }
  1337. if($data =~ /'defaultAlbumArt':'res\/Home_OnlineServices.png'/)
  1338. {
  1339. readingsBulkUpdate($hash, "input", "Online Services");
  1340. }
  1341. readingsEndUpdate($hash, 1);
  1342. if($data =~ /'title':'\\'(.+)\\''/)
  1343. {
  1344. if((ReadingsVal($name, "input", "") eq "Media Library") or (ReadingsVal($name, "input", "") eq "Spotify"))
  1345. {
  1346. readingsSingleUpdate($hash, "playerTitle", PHILIPS_AUDIO_html2txt($1), 1);
  1347. delete $hash->{READINGS}{playerRadioStationInfo};
  1348. }
  1349. elsif(ReadingsVal($name, "input", "") eq "Internet Radio")
  1350. {
  1351. readingsSingleUpdate($hash, "playerRadioStationInfo", PHILIPS_AUDIO_html2txt($1), 1);
  1352. delete $hash->{READINGS}{playerTitle};
  1353. }
  1354. }
  1355. elsif($data =~ /'title':'(.+)'/)
  1356. {
  1357. if((ReadingsVal($name, "input", "") eq "Media Library") or (ReadingsVal($name, "input", "") eq "Spotify"))
  1358. {
  1359. readingsSingleUpdate($hash, "playerTitle", PHILIPS_AUDIO_html2txt($1), 1);
  1360. delete $hash->{READINGS}{playerRadioStationInfo};
  1361. }
  1362. elsif(ReadingsVal($name, "input", "") eq "Internet Radio")
  1363. {
  1364. readingsSingleUpdate($hash, "playerRadioStationInfo", PHILIPS_AUDIO_html2txt($1), 1);
  1365. delete $hash->{READINGS}{playerTitle};
  1366. }
  1367. }
  1368. else
  1369. {
  1370. delete $hash->{READINGS}{playerTitle};
  1371. delete $hash->{READINGS}{playerRadioStationInfo};
  1372. }
  1373. if($data =~ /'subTitle':'(.+)'/)
  1374. {
  1375. if((ReadingsVal($name, "input", "") eq "Media Library") or (ReadingsVal($name, "input", "") eq "Spotify"))
  1376. {
  1377. readingsSingleUpdate($hash, "playerAlbum", PHILIPS_AUDIO_html2txt($1), 1);
  1378. delete $hash->{READINGS}{playerRadioStation};
  1379. }
  1380. else
  1381. {
  1382. readingsSingleUpdate($hash, "playerRadioStation", PHILIPS_AUDIO_html2txt($1), 1);
  1383. delete $hash->{READINGS}{playerAlbum};
  1384. }
  1385. }
  1386. else
  1387. {
  1388. delete $hash->{READINGS}{playerRadioStation};
  1389. delete $hash->{READINGS}{playerAlbum};
  1390. }
  1391. if($data =~ /'albumArt':'(.+)'/)
  1392. {
  1393. readingsSingleUpdate($hash, "playerAlbumArt", $1, 1);
  1394. }
  1395. else
  1396. {
  1397. delete $hash->{READINGS}{playerAlbumArt};
  1398. }
  1399. readingsBeginUpdate($hash);
  1400. if($data =~ /'volume':(.+),/)
  1401. {
  1402. readingsBulkUpdate($hash, "volumeStraight", $1);
  1403. readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $1));
  1404. }
  1405. else
  1406. {
  1407. readingsBulkUpdate($hash, "volumeStraight", "0");
  1408. readingsBulkUpdate($hash, "volume", "0");
  1409. }
  1410. readingsEndUpdate($hash, 1);
  1411. if($data =~ /'elapsetime':(.+),/)
  1412. {
  1413. if(int($1) < 3600)
  1414. {
  1415. readingsSingleUpdate($hash, "playerPlayTime", strftime("%M:\%S", gmtime($1)), 1);
  1416. }
  1417. else
  1418. {
  1419. readingsSingleUpdate($hash, "playerPlayTime", strftime("\%H:\%M:\%S", gmtime($1)), 1);
  1420. }
  1421. }
  1422. else
  1423. {
  1424. delete $hash->{READINGS}{playerPlayTime};
  1425. }
  1426. if($data =~ /'totaltime':(.+),/)
  1427. {
  1428. # Playing radio delivers that total time
  1429. if($1 eq "65535")
  1430. {
  1431. delete $hash->{READINGS}{playerTotalPlayTime};
  1432. }
  1433. elsif(int($1) < 3600)
  1434. {
  1435. readingsSingleUpdate($hash, "playerTotalPlayTime", strftime("%M:\%S", gmtime($1)), 1);
  1436. }
  1437. else
  1438. {
  1439. readingsSingleUpdate($hash, "playerTotalPlayTime", strftime("\%H:\%M:\%S", gmtime($1)), 1);
  1440. }
  1441. }
  1442. else
  1443. {
  1444. delete $hash->{READINGS}{playerTotalPlayTime};
  1445. }
  1446. if($data =~ /'muteStatus':(.+),/)
  1447. {
  1448. if($1 == 1)
  1449. {
  1450. readingsSingleUpdate($hash, "mute", "on", 1);
  1451. }
  1452. else
  1453. {
  1454. readingsSingleUpdate($hash, "mute", "off", 1);
  1455. }
  1456. }
  1457. # typo in the (buggy) Streamium firmware...
  1458. if($data =~ /'playStaus':(.+),/)
  1459. {
  1460. if($1 == 1)
  1461. {
  1462. readingsSingleUpdate($hash, "playerPlaying", "yes", 1);
  1463. }
  1464. else
  1465. {
  1466. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1467. readingsSingleUpdate($hash, "playerPlaying", "no", 1);
  1468. }
  1469. }
  1470. else
  1471. {
  1472. delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}});
  1473. readingsSingleUpdate($hash, "playerPlaying", "no", 1);
  1474. }
  1475. }
  1476. elsif($cmd eq "homestatus")
  1477. {
  1478. # Homestatus answers with
  1479. # {'command':'NOTHING',\n'value':0}
  1480. # Do nothing ?
  1481. }
  1482. elsif ($cmd eq "getMediaRendererDesc")
  1483. {
  1484. if($data =~ /<manufacturer>(.+)<\/manufacturer>/)
  1485. {
  1486. $hash->{helper}{dInfo}{MANUFACTURER} = $1;
  1487. }
  1488. if($data =~ /<manufacturerURL>(.+)<\/manufacturerURL>/)
  1489. {
  1490. $hash->{helper}{dInfo}{MANUFACTURER_URL} = $1;
  1491. }
  1492. if($data =~ /<manufacturerURL>(.+)<\/manufacturerURL>/)
  1493. {
  1494. $hash->{helper}{dInfo}{MANUFACTURER_URL} = $1;
  1495. }
  1496. if($data =~ /<presentationURL>(.+)<\/presentationURL>/)
  1497. {
  1498. $hash->{helper}{dInfo}{PRESENTATION_URL} = $1;
  1499. }
  1500. if($data =~ /<deviceType>(.+)<\/deviceType>/)
  1501. {
  1502. $hash->{helper}{dInfo}{UPNP_DEVICE_TYPE} = $1;
  1503. }
  1504. if($data =~ /<friendlyName>(.+)<\/friendlyName>/)
  1505. {
  1506. $hash->{helper}{dInfo}{FRIENDLY_NAME} = $1;
  1507. }
  1508. if($data =~ /<UDN>(.+)<\/UDN>/)
  1509. {
  1510. my $uuid = uc($1);
  1511. $uuid =~ s/UUID://g;
  1512. $hash->{helper}{dInfo}{UUID} = $uuid;
  1513. }
  1514. if($data =~ /<UPC>(.+)<\/UPC>/)
  1515. {
  1516. $hash->{helper}{dInfo}{UPC} = $1;
  1517. }
  1518. if($data =~ /<modelName>(.+)<\/modelName>/)
  1519. {
  1520. $hash->{helper}{dInfo}{MODEL_NAME} = $1;
  1521. }
  1522. if($data =~ /<modelNumber>(.+)<\/modelNumber>/)
  1523. {
  1524. $hash->{helper}{dInfo}{MODEL_NUMBER} = $1;
  1525. }
  1526. if($data =~ /<serialNumber>(.+)<\/serialNumber>/)
  1527. {
  1528. $hash->{helper}{dInfo}{SERIAL_NUMBER} = uc($1);
  1529. }
  1530. if($data =~ /<modelDescription>(.+)<\/modelDescription>/)
  1531. {
  1532. $hash->{helper}{dInfo}{MODEL_DESCRIPTION} = $1;
  1533. }
  1534. # Replace \n, \r, \t from the string for XML parsing
  1535. # replace \n by ""
  1536. $data =~ s/\n//g;
  1537. # replace \t by ""
  1538. $data =~ s/\t//g;
  1539. # replace \r by ""
  1540. $data =~ s/\r//g;
  1541. if($data =~ /<iconList>(.+?)<\/iconList>/)
  1542. {
  1543. my $address = $hash->{IP_ADDRESS};
  1544. my $port = "";
  1545. if (uc($hash->{MODEL}) eq "NP3700")
  1546. {
  1547. $port = 7123;
  1548. }
  1549. elsif
  1550. (
  1551. (uc($hash->{MODEL}) eq "NP3500") or
  1552. (uc($hash->{MODEL}) eq "NP3900") or
  1553. (uc($hash->{MODEL}) eq "AW9000")
  1554. )
  1555. {
  1556. $port = 49153;
  1557. }
  1558. my $i = 1;
  1559. while ($data =~ /<url>(.+?)<\/url>/g)
  1560. {
  1561. # May have several urls according to the UPNP/DLNA standard
  1562. $hash->{helper}{dInfo}{"DEVICE_ICON_$i"} = "http://$address:$port/$1";
  1563. $i++;
  1564. }
  1565. }
  1566. }
  1567. elsif ($cmd eq "getPresets")
  1568. {
  1569. $hash->{helper}{TOTALINETRADIOPRESETS} = 0 if(not defined($hash->{helper}{TOTALINETRADIOPRESETS}));
  1570. # This command must be processed hierarchicaly through the navigation path
  1571. if($hash->{helper}{cmdStep} == 1)
  1572. {
  1573. $hash->{helper}{cmdStep} = 2;
  1574. # Internet radio
  1575. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getPresets", "noArg");
  1576. }
  1577. elsif($hash->{helper}{cmdStep} == 2)
  1578. {
  1579. $hash->{helper}{cmdStep} = 3;
  1580. # Presets
  1581. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","getPresets", "noArg");
  1582. }
  1583. elsif($hash->{helper}{cmdStep} == 3)
  1584. {
  1585. my $listedItems; # Visible Items in the display. Max 8
  1586. my $nextreqURL;
  1587. my $i;
  1588. my $presetID = 0;
  1589. my $presetName;
  1590. # Parse first 8 Presets
  1591. if($data =~ /<title>Error<\/title>/)
  1592. {
  1593. # In case on presets defined the player returns an Error
  1594. # Do nothing
  1595. $hash->{helper}{TOTALINETRADIOPRESETS} = 0;
  1596. readingsSingleUpdate($hash, "totalPresets", "0", 1);
  1597. delete $hash->{READINGS}{Reading_presets};
  1598. }
  1599. else
  1600. {
  1601. if ($data =~ /'nextrequrl':'(.+?)',/)
  1602. {
  1603. $nextreqURL = $1;
  1604. #Log3 $name, 5, "NextreqURL: $nextreqURL";
  1605. }
  1606. if ($data =~ /'totalListCount':(.+),/)
  1607. {
  1608. $hash->{helper}{TOTALINETRADIOPRESETS} = $1;
  1609. readingsSingleUpdate($hash, "totalPresets", $1, 1);
  1610. #Log3 $name, 5, "ListedItems: $listedItems";
  1611. }
  1612. $data =~ s/\R//g; # Remove new lines
  1613. readingsBeginUpdate($hash);
  1614. while ($data =~ /{'title':'(.+?)',/g)
  1615. {
  1616. $presetName = $1;
  1617. if($data =~ /'id':(.+?),/g)
  1618. {
  1619. $presetID = $1;
  1620. }
  1621. if ($presetID ne "" and $presetName ne "")
  1622. {
  1623. readingsBulkUpdate($hash, sprintf(".inetRadioPreset_%02d", $presetID), $presetName);
  1624. }
  1625. }
  1626. readingsEndUpdate($hash, 1);
  1627. if($presetID < ($hash->{helper}{TOTALINETRADIOPRESETS})) # Maximum listed items = 8. Get the next items by sending the nextreqURL
  1628. {
  1629. # External Command. Not from buffer timer.
  1630. $hash->{helper}{fromSendCommandBuffer} = 0;
  1631. PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getPresets", "noArg");
  1632. }
  1633. else
  1634. {
  1635. readingsSingleUpdate($hash, "playerListStatus", "ready", 1);
  1636. delete $hash->{READINGS}{Reading_presets};
  1637. delete $hash->{READINGS}{readingPresets};
  1638. $hash->{helper}{cmdStep} = 4;
  1639. # Finished
  1640. }
  1641. }
  1642. }
  1643. }
  1644. elsif ($cmd eq "getFavorites")
  1645. {
  1646. $hash->{helper}{TOTALINETRADIOFAVORITES} = 0 if(not defined($hash->{helper}{TOTALINETRADIOFAVORITES}));
  1647. # This command must be processed hierarchicaly through the navigation path
  1648. if($hash->{helper}{cmdStep} == 1)
  1649. {
  1650. $hash->{helper}{cmdStep} = 2;
  1651. # Internet radio
  1652. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getFavorites", "noArg");
  1653. }
  1654. elsif($hash->{helper}{cmdStep} == 2)
  1655. {
  1656. $hash->{helper}{cmdStep} = 3;
  1657. # Favorites
  1658. PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","getFavorites", "noArg");
  1659. }
  1660. elsif($hash->{helper}{cmdStep} == 3)
  1661. {
  1662. if($data =~ /<title>Error<\/title>/)
  1663. {
  1664. # In case on presets defined the player returns an Error
  1665. # Do nothing
  1666. $hash->{helper}{TOTALINETRADIOFAVORITES} = 0;
  1667. readingsSingleUpdate($hash, "totalFavorites", "0", 1);
  1668. delete $hash->{READINGS}{Reading_favorites};
  1669. }
  1670. else
  1671. {
  1672. my $listedItems; # Visible Items in the display. Max 8
  1673. my $nextreqURL;
  1674. my $i;
  1675. my $favoriteID = 0;
  1676. my $favoriteName;
  1677. # Parse first 8 Presets
  1678. if ($data =~ /'nextrequrl':'(.+?)',/)
  1679. {
  1680. $nextreqURL = $1;
  1681. #Log3 $name, 5, "NextreqURL: $nextreqURL";
  1682. }
  1683. if ($data =~ /'totalListCount':(.+),/)
  1684. {
  1685. $hash->{helper}{TOTALINETRADIOFAVORITES} = $1;
  1686. readingsSingleUpdate($hash, "totalFavorites", $1, 1);
  1687. }
  1688. $data =~ s/\R//g; # Remove new lines
  1689. readingsBeginUpdate($hash);
  1690. while($data =~ /{'title':'(.+?)',/g)
  1691. {
  1692. $favoriteName = $1;
  1693. if($data =~ /'id':(.+?),/g)
  1694. {
  1695. $favoriteID = $1;
  1696. }
  1697. if ($favoriteID ne "" and $favoriteName ne "")
  1698. {
  1699. readingsBulkUpdate($hash, sprintf(".inetRadioFavorite_%02d", $favoriteID), $favoriteName);
  1700. }
  1701. }
  1702. readingsEndUpdate($hash, 1);
  1703. #Log3 $name, 5, "FavoriteIDNachLoop: $favoriteID";
  1704. if($favoriteID < ($hash->{helper}{TOTALINETRADIOFAVORITES})) # Maximum listed items = 8. Get the next items by sending the nextreqURL
  1705. {
  1706. # External Command. Not from buffer timer.
  1707. $hash->{helper}{fromSendCommandBuffer} = 0;
  1708. PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getFavorites", "noArg");
  1709. }
  1710. else
  1711. {
  1712. readingsSingleUpdate($hash, "playerListStatus", "ready", 1);
  1713. delete $hash->{READINGS}{Reading_favorites};
  1714. delete $hash->{READINGS}{readingFavorites};
  1715. $hash->{helper}{cmdStep} = 4;
  1716. # Finished
  1717. }
  1718. }
  1719. }
  1720. }
  1721. elsif($cmd =~ /input|selectStream/)
  1722. {
  1723. $data =~ s/\R//g; # Remove new lines for regex
  1724. if($arg eq "---")
  1725. {
  1726. # Do nothing
  1727. }
  1728. elsif($arg =~ /internetRadio|onlineServices|mediaLibrary|(\d{3})_(.+?)_|lvl_(.+?)/) # 000_[c|i]_******
  1729. {
  1730. # don't update menu content if playable item was chosen
  1731. if (defined $1)
  1732. {
  1733. if ($2 eq "i")
  1734. {
  1735. $hash->{helper}{networkRequest} = "idle";
  1736. $hash->{helper}{playerState} = "playing";
  1737. # Stream selected. Manual operation finished.
  1738. $hash->{helper}{manualOperation} = 0;
  1739. readingsBeginUpdate($hash);
  1740. readingsBulkUpdate ($hash, "networkRequest", "idle");
  1741. readingsBulkUpdate ($hash, "playerListStatus", "ready");
  1742. readingsBulkUpdate ($hash, "playerState", "playing");
  1743. readingsBulkUpdate ($hash, ".manualOperation", "no");
  1744. readingsEndUpdate ($hash, 1);
  1745. PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg");
  1746. # Stream selected trigger getStatus
  1747. PHILIPS_AUDIO_ResetTimer($hash); # getStatus
  1748. return;
  1749. }
  1750. }
  1751. if ($data =~ /{'command':'NOTHING','value':0}|<title>Error<\/title>/)
  1752. {
  1753. my $errorMessage = "Player responded with unspecified error";
  1754. if($data =~ /alert\(\'(.*?)\'\);|\'command\':\'(.*?)\'/)
  1755. {
  1756. $errorMessage = $1;
  1757. }
  1758. # Delete old readings
  1759. delete $hash->{READINGS}{$_} foreach (grep /listItem_...$/, keys %{$hash->{READINGS}});
  1760. delete $hash->{READINGS}{$_} foreach (grep /.listItemTarget_...$/, keys %{$hash->{READINGS}});
  1761. $hash->{helper}{networkRequest} = "idle";
  1762. $hash->{helper}{manualOperation} = 0;
  1763. readingsBeginUpdate($hash);
  1764. readingsBulkUpdate ($hash, "networkRequest", "idle");
  1765. readingsBulkUpdate ($hash, "listItem_001", "$errorMessage");
  1766. readingsBulkUpdate ($hash, ".listItemTarget_001", "$hash->{helper}{currentNavUrl}");
  1767. readingsBulkUpdate ($hash, "playerListStatus", "ready");
  1768. readingsBulkUpdate ($hash, ".manualOperation", "no");
  1769. readingsEndUpdate ($hash, 1);
  1770. return "Player response: $errorMessage.";
  1771. }
  1772. my $listDepthLevel = 0;
  1773. my $playerListTotalCount = 0;
  1774. my $listId = 0;
  1775. my $listItems = "";
  1776. readingsBeginUpdate($hash);
  1777. if($data =~ /var listdetails = \{(.*)\};/)
  1778. {
  1779. my $listdetails_temp = $1;
  1780. my $listdetails = $listdetails_temp;
  1781. if($listdetails =~ /'totalitems':(.*?),/)
  1782. {
  1783. readingsBulkUpdate($hash, ".listTotalItems", "$1");
  1784. }
  1785. $listdetails = $listdetails_temp;
  1786. if($listdetails =~ /'totalListCount':(.*?),/)
  1787. {
  1788. $playerListTotalCount = $1;
  1789. $hash->{helper}{playerListTotalCount} = $1;
  1790. readingsBulkUpdate($hash, "playerListTotalCount", "$1");
  1791. }
  1792. $listdetails = $listdetails_temp;
  1793. if($listdetails =~ /'depthlevel':(.*?),/)
  1794. {
  1795. $listDepthLevel = $1;
  1796. readingsBulkUpdate($hash, ".listDepthLevel", "$1");
  1797. }
  1798. }
  1799. if($data =~ /var listItem = \{(.*)\};/)
  1800. {
  1801. my $listItems_temp = $1;
  1802. $listItems = $listItems_temp;
  1803. if($listItems =~ /'nextrequrl':'(.*?)'/)
  1804. {
  1805. $hash->{helper}{nextUrl} = $1;
  1806. readingsBulkUpdate($hash, ".listNextUrl", "$1");
  1807. }
  1808. $listItems = $listItems_temp;
  1809. if($listItems =~ /'prevUrl':'(.*?)',/)
  1810. {
  1811. if($1 ne "")
  1812. {
  1813. $hash->{helper}{prevUrl} = $1;
  1814. readingsBulkUpdate($hash, ".listPrevUrl", "$1");
  1815. }
  1816. else
  1817. {
  1818. $hash->{helper}{prevUrl} = "-";
  1819. readingsBulkUpdate($hash, ".listPrevUrl", "-");
  1820. }
  1821. }
  1822. readingsEndUpdate($hash, 1);
  1823. $listItems = $listItems_temp;
  1824. if($listItems =~ /'items': \[(.*)\]/)
  1825. {
  1826. # Delete old readings
  1827. delete $hash->{READINGS}{$_} foreach (grep /listItem_...$/, keys %{$hash->{READINGS}});
  1828. delete $hash->{READINGS}{$_} foreach (grep /.listItemTarget_...$/, keys %{$hash->{READINGS}});
  1829. # Predefine all listItem readings with "reading..."
  1830. readingsBeginUpdate($hash);
  1831. for(my $i = 1; ($i < int($hash->{helper}{playerListTotalCount}) + 1) and ($i < AttrVal($name, "maxListItems", 100) + 1); $i++)
  1832. {
  1833. readingsBulkUpdate($hash, "listItem_".sprintf("%03s", $i), "reading...");
  1834. }
  1835. my $items = $1;
  1836. while ($items =~ /\{(.*?)\}/g)
  1837. {
  1838. my $item = $1;
  1839. my $title = "";
  1840. if($item =~ /'title':'(.*?)',/)
  1841. {
  1842. $title = PHILIPS_AUDIO_html2txt($1);
  1843. }
  1844. if($item =~ /'id':(.*?),/)
  1845. {
  1846. $listId = $1;
  1847. }
  1848. my $itemTarget = "";
  1849. if($item =~ /'target':'(.*?)'/)
  1850. {
  1851. $itemTarget = $1;
  1852. readingsBulkUpdate($hash, ".listItemTarget_".sprintf("%03s", int($listId)), $1);
  1853. }
  1854. if(substr($itemTarget, -1) eq "1")
  1855. {
  1856. $title = "i_" . $title; # item
  1857. }
  1858. else
  1859. {
  1860. $title = "c_" . $title; # container
  1861. }
  1862. readingsBulkUpdate($hash, "listItem_".sprintf("%03s", int($listId)), $title);
  1863. }
  1864. readingsEndUpdate($hash, 1);
  1865. }
  1866. }
  1867. if($listId < $hash->{helper}{playerListTotalCount})
  1868. {
  1869. # External Command. Not from buffer timer.
  1870. $hash->{helper}{fromSendCommandBuffer} = 0;
  1871. PHILIPS_AUDIO_SendCommand($hash, "$hash->{helper}{nextUrl}", "", "selectStream", "list");
  1872. }
  1873. else
  1874. {
  1875. $hash->{helper}{networkRequest} = "idle";
  1876. readingsBeginUpdate($hash);
  1877. readingsBulkUpdate ($hash, "networkRequest", "idle");
  1878. readingsBulkUpdate ($hash, "playerListStatus", "ready");
  1879. readingsEndUpdate ($hash, 1);
  1880. }
  1881. }
  1882. elsif($arg =~ /list/)
  1883. {
  1884. if ($data =~ /{'command':'NOTHING','value':0}|<title>Error<\/title>/)
  1885. {
  1886. $hash->{helper}{networkRequest} = "idle";
  1887. readingsSingleUpdate($hash, "networkRequest", "idle", 1);
  1888. Log3 $name, 3, "PHILIPS_AUDIO ($name) - Player response: Media Library change not successful.";
  1889. return "Player response: Media Library change not successful.";
  1890. }
  1891. $hash->{helper}{networkRequest} = "busy";
  1892. readingsBeginUpdate($hash);
  1893. readingsBulkUpdate($hash, "networkRequest", "busy");
  1894. $data =~ s/\R//g; # Remove new lines for regex
  1895. my $listDepthLevel = 0;
  1896. my $playerListTotalCount = 0;
  1897. my $listId = 0;
  1898. my $listItems = "";
  1899. $listItems = $data;
  1900. if($listItems =~ /'nextrequrl':'(.*?)'/)
  1901. {
  1902. $hash->{helper}{nextUrl} = $1;
  1903. readingsBulkUpdate($hash, ".listNextUrl", "$1");
  1904. }
  1905. $listItems = $data;
  1906. if($listItems =~ /'prevUrl':'(.*?)',/)
  1907. {
  1908. if($1 ne "")
  1909. {
  1910. $hash->{helper}{prevUrl} = $1;
  1911. readingsBulkUpdate($hash, ".listPrevUrl", "$1");
  1912. }
  1913. else
  1914. {
  1915. $hash->{helper}{prevUrl} = "-";
  1916. readingsBulkUpdate($hash, ".listPrevUrl", "-");
  1917. }
  1918. }
  1919. $listItems = $data;
  1920. if($listItems =~ /'items': \[(.*)\]/)
  1921. {
  1922. my $items = $1;
  1923. while ($items =~ /\{(.*?)\}/g)
  1924. {
  1925. my $item_temp = $1;
  1926. my $item = $item_temp;
  1927. my $title = "";
  1928. if($item =~ /'title':'(.*?)',/)
  1929. {
  1930. $title = PHILIPS_AUDIO_html2txt($1);
  1931. }
  1932. $item = $item_temp;
  1933. if($item =~ /'id':(.*?),/)
  1934. {
  1935. $listId = $1;
  1936. }
  1937. $item = $item_temp;
  1938. my $itemTarget = "";
  1939. if($item =~ /'target':'(.*?)'/)
  1940. {
  1941. $itemTarget = $1;
  1942. readingsBulkUpdate($hash, ".listItemTarget_".sprintf("%03s", int($listId)), $1);
  1943. }
  1944. if(substr($itemTarget, -1) eq "1")
  1945. {
  1946. $title = "i_" . $title; # item
  1947. }
  1948. else
  1949. {
  1950. $title = "c_" . $title; # container
  1951. }
  1952. readingsBulkUpdate($hash, "listItem_".sprintf("%03s", int($listId)), $title);
  1953. last if($listId eq AttrVal($name, "maxListItems", 100));
  1954. }
  1955. }
  1956. if(($listId < $hash->{helper}{playerListTotalCount}) && ($listId < AttrVal($name, "maxListItems", 100)))
  1957. {
  1958. # External Command. Not from buffer timer.
  1959. $hash->{helper}{fromSendCommandBuffer} = 0;
  1960. PHILIPS_AUDIO_SendCommand($hash, "$hash->{helper}{nextUrl}", "", "selectStream", "list");
  1961. }
  1962. else
  1963. {
  1964. $hash->{helper}{networkRequest} = "idle";
  1965. readingsBulkUpdate($hash, "networkRequest", "idle");
  1966. readingsBulkUpdate($hash, "playerListStatus", "ready");
  1967. }
  1968. readingsEndUpdate($hash, 1);
  1969. }
  1970. }
  1971. $hash->{helper}{networkRequest} = "idle";
  1972. readingsSingleUpdate($hash, "networkRequest", "idle", 1);
  1973. }
  1974. return;
  1975. }
  1976. #############################
  1977. # converts straight volume in percentage volume (volumestraightmin .. volumestraightmax => 0 .. 100%)
  1978. sub PHILIPS_AUDIO_volume_rel2abs
  1979. {
  1980. my ($hash, $percentage) = @_;
  1981. return int($percentage * 64 / 100);
  1982. }
  1983. #############################
  1984. # converts relative volume to "straight" volume (0 .. 100% => volumestraightmin .. volumestraightmax)
  1985. sub PHILIPS_AUDIO_volume_abs2rel
  1986. {
  1987. my ($hash, $absolute) = @_;
  1988. return int($absolute * 100 / 64);
  1989. }
  1990. #############################
  1991. # Restarts the internal status request timer according to the given interval or current receiver state
  1992. sub PHILIPS_AUDIO_ResetTimer
  1993. {
  1994. my ($hash, $interval) = @_;
  1995. RemoveInternalTimer($hash, "PHILIPS_AUDIO_GetStatus");
  1996. if($hash->{helper}{DISABLED} == 0)
  1997. {
  1998. if(defined($interval))
  1999. {
  2000. InternalTimer(gettimeofday() + $interval, "PHILIPS_AUDIO_GetStatus", $hash);
  2001. }
  2002. elsif((exists($hash->{READINGS}{presence}{VAL}) and $hash->{READINGS}{presence}{VAL} eq "present") and (exists($hash->{READINGS}{power}{VAL}) and $hash->{READINGS}{power}{VAL} eq "on"))
  2003. {
  2004. InternalTimer(gettimeofday() + $hash->{helper}{ON_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash);
  2005. }
  2006. else
  2007. {
  2008. InternalTimer(gettimeofday() + $hash->{helper}{OFF_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash);
  2009. }
  2010. }
  2011. return;
  2012. }
  2013. #############################
  2014. # convert all HTML entities into UTF-8 equivalent
  2015. sub PHILIPS_AUDIO_html2txt
  2016. {
  2017. my ($string) = @_;
  2018. $string =~ s/(&amp;amp;quot;|&amp;quot;|&quot;|\\")/\"/g;
  2019. $string =~ s/(&amp;amp;|&amp;)/&/g;
  2020. $string =~ s/&nbsp;/ /g;
  2021. $string =~ s/(&apos;|\\'|\')/'/g;
  2022. $string =~ s/(\xe4|&auml;)/ä/g;
  2023. $string =~ s/(\xc4|&Auml;)/Ä/g;
  2024. $string =~ s/(\xf6|&ouml;)/ö/g;
  2025. $string =~ s/(\xd6|&Ouml;)/Ö/g;
  2026. $string =~ s/(\xfc|&uuml;)/ü/g;
  2027. $string =~ s/(\xdc|&Uuml;)/Ü/g;
  2028. $string =~ s/(\xdf|&szlig;)/ß/g;
  2029. return $string;
  2030. }
  2031. sub PHILIPS_AUDIO_getMediaRendererDesc
  2032. {
  2033. # queries the addition model descriptions
  2034. my ($hash) = @_;
  2035. my $name = $hash->{NAME};
  2036. Log3 $name, 5, "PHILIPS_AUDIO ($name) - execute nonblocking \"MediaRendererDesc\"";
  2037. my $url = "";
  2038. my $port = "";
  2039. if(uc($hash->{MODEL}) eq "NP3700")
  2040. {
  2041. $url = "http://$hash->{IP_ADDRESS}:7123/DeviceDescription.xml";
  2042. }
  2043. elsif
  2044. (
  2045. (uc($hash->{MODEL}) eq "NP3500") or
  2046. (uc($hash->{MODEL}) eq "NP3900") or
  2047. (uc($hash->{MODEL}) eq "AW9000")
  2048. )
  2049. {
  2050. $url = "http://$hash->{IP_ADDRESS}:49153/nmrDescription.xml";
  2051. }
  2052. else
  2053. {
  2054. return "Unknown Device.";
  2055. }
  2056. HttpUtils_NonblockingGet({
  2057. url => $url,
  2058. timeout => AttrVal($name, "requestTimeout", 10),
  2059. noshutdown => 1,
  2060. data => "",
  2061. loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
  2062. hash => $hash,
  2063. cmd => "getMediaRendererDesc",
  2064. arg => "noArg",
  2065. callback => \&PHILIPS_AUDIO_ParseResponse
  2066. });
  2067. return;
  2068. }
  2069. 1;
  2070. =pod
  2071. =item device
  2072. =item summary controls a Philips Streamium Network Player in a local network
  2073. =item summary_DE steuert einen Philips Streamium Netzwerkplayer im lokalen Netzwerk
  2074. =begin html
  2075. <a name="PHILIPS_AUDIO"></a>
  2076. <h3>PHILIPS_AUDIO</h3>
  2077. <ul>
  2078. <a name="PHILIPS_AUDIOdefine"></a>
  2079. <b>Define</b>
  2080. <br><br>
  2081. <ul>
  2082. <code>
  2083. define &lt;name&gt; PHILIPS_AUDIO &lt;device model&gt; &lt;ip-address&gt; [&lt;status_interval&gt;]<br><br>
  2084. define &lt;name&gt; PHILIPS_AUDIO &lt;device model&gt; [&lt;off_status_interval&gt;] [&lt;on_status_interval&gt;]
  2085. </code>
  2086. <br><br>
  2087. This module controls a Philips Audio Player e.g. MCi, Streamium or Fidelio and potentially any other device controlled by the "myRemote" app.<br>
  2088. You might also check, opening the following URL in the browser: http://[ip number of your device]:8889/index
  2089. <br><br>
  2090. (Tested on: AW9000, NP3500, NP3700 and NP3900)
  2091. <br><br>
  2092. Example:<br><br>
  2093. <ul>
  2094. <code>
  2095. define player PHILIPS_AUDIO NP3900 192.168.0.15<br><br>
  2096. # With custom status interval of 60 seconds<br>
  2097. define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b><br><br>
  2098. # With custom "off"-interval of 60 seconds and "on"-interval of 10 seconds<br>
  2099. define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60 10</b><br>
  2100. </code>
  2101. <br>
  2102. <i>Note: Due to slow command processing by the player itself the minimum interval is <b>limited to 5 seconds</b>. More frequent polling might cause device freezes.</i>
  2103. </ul>
  2104. </ul>
  2105. <br>
  2106. <a name="PHILIPS_AUDIOset"></a>
  2107. <b>Set</b>
  2108. <ul>
  2109. <code>
  2110. set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]
  2111. </code><br><br>
  2112. <i>Note: Commands and parameters are case sensitive.</i><br>
  2113. <ul><br>
  2114. <li><b>favoriteAdd</b> &ndash; Adds currently played Internet Radio stream to favorites</li>
  2115. <li><b>favoriteRemove</b> &ndash; Removes currently played Internet Radio stream from favorites</li>
  2116. <li><b>getFavorites</b> &ndash; Reads stored favorites from the device (may take some time...)</li>
  2117. <li><b>getMediaRendererDesc</b> &ndash; Reads device specific information (stored in the deviceInfo reading)</li>
  2118. <li><b>getPresets</b> &ndash; Reads stored presets from the device (may take some time...)</li>
  2119. <li><b>input</b> &ndash; Selects the following input</li>
  2120. <ul>
  2121. <li><b>analogAux</b> &ndash; Selects the analog AUX input (AW9000 only)</li>
  2122. <li><b>digital1Coaxial</b> &ndash; Selects the digital coaxial input (AW9000 only)</li>
  2123. <li><b>digital2Optical</b> &ndash; Selects the digital optical input (AW9000 only)</li>
  2124. <li><b>internetRadio</b> &ndash; Selects the Internet Radio input</li>
  2125. <li><b>mediaLibrary</b> &ndash; Selects the Media Library input (UPnP/DLNA server) (not available on AW9000)</li>
  2126. <li><b>mp3Link</b> &ndash; Selects the analog MP3 Link input (not available on AW9000)</li>
  2127. <li><b>onlineServices</b> &ndash; Selects the Online Services input</li>
  2128. </ul>
  2129. <li><b>mute [ on | off ]</b> &ndash; Mutes/unmutes the device</li>
  2130. <li><b>player</b> &ndash; Player related commands</li>
  2131. <ul>
  2132. <li><b>next</b> &ndash; Selects next audio stream</li>
  2133. <li><b>prev</b> &ndash; Selects previous audio stream</li>
  2134. <li><b>play-pause</b> &ndash; Plays/pauses the current audio stream</li>
  2135. <li><b>stop</b> &ndash; Stops the current audio stream</li>
  2136. </ul>
  2137. <li><b>repeat [ single | all | off]</b> &ndash; Selects the repeate mode</li>
  2138. <li><b>selectFavorite [ name ]</b> &ndash; Selects a favorite. Empty if no favorites found. (see also getFavorites)</li>
  2139. <li><b>selectFavoriteByNumber [ number ]</b> &ndash; Selects a favorite by its number. Empty if no favorites found. (see also getFavorites)</li>
  2140. <li><b>selectPreset [ name ]</b> &ndash; Selects a preset. Empty if no presets found. (see also getPresets)</li>
  2141. <li><b>selectPresetByNumber [ number ]</b> &ndash; Selects a preset by its number. Empty if no presets found. (see also getPresets)</li>
  2142. <li><b>selectStream [ name ]</b> &ndash; Context-sensitive. Selects a stream depending on the current input and player list content. A 'c'-prefix represents a 'container' (directory). An 'i'-prefix represents an 'item' (audio stream).</li>
  2143. <li><b>shuffle [ on | off ]</b> &ndash; Sets the shuffle mode</li>
  2144. <li><b>standbyButton</b> &ndash; Emulates the standby button. Toggles between standby and power on</li>
  2145. <li><b>volume</b> &ndash; Sets the relative volume 0...100%</li>
  2146. <li><b>volumeDown</b> &ndash; Sets the device specific volume by one decrement</li>
  2147. <li><b>volumeStraight</b> &ndash; Sets the device specific absolute volume 0...64</li>
  2148. <li><b>volumeUp</b> &ndash; Sets the device specific volume by one increment</li>
  2149. </ul>
  2150. </ul>
  2151. <br>
  2152. <a name="PHILIPS_AUDIOget"></a>
  2153. <b>Get</b>
  2154. <ul>
  2155. <code>
  2156. get &lt;name&gt; &lt;reading&gt &lt;reading name&gt;
  2157. </code>
  2158. <ul>
  2159. <br>
  2160. <li><b>deviceInfo</b> &ndash; Returns device specific information</li>
  2161. <li><b>reading</b></li>
  2162. <ul>
  2163. <li><b>input</b> &ndash; Returns current input or '-' if not playing</li>
  2164. <li><b>listItem_xxx</b> &ndash; Returns player list item (limited to 999 entries)</li>
  2165. <li><b>networkError</b> &ndash; Shows an occured current network error</li>
  2166. <li><b>networkRequest</b> &ndash; Shows current network activity (idle/busy)</li>
  2167. <li><b>power</b> &ndash; Returns power status (on/off)</li>
  2168. <li><b>playerAlbum</b> &ndash; Returns the album name of played stream</li>
  2169. <li><b>playerAlbumArt</b> &ndash; Returns the album art of played audio stream</li>
  2170. <li><b>playerListStatus</b> &ndash; Returns current player list status (busy/ready)</li>
  2171. <li><b>playerListTotalCount</b> &ndash; Returns number of player list entries</li>
  2172. <li><b>playerPlayTime</b> &ndash; Returns audio stream duration</li>
  2173. <li><b>playerPlaying</b> &ndash; Returns current player playing state (yes/no)</li>
  2174. <li><b>playerRadioStation</b> &ndash; Returns the name of played radio station</li>
  2175. <li><b>playerRadioStationInfo</b> &ndash; Returns additional info of the played radio station</li>
  2176. <li><b>playerRepeat</b> &ndash; Returns current repeat mode (off/single/all)</li>
  2177. <li><b>playerShuffle</b> &ndash; Returns current shuffle mode (on/off)</li>
  2178. <li><b>playerState</b> &ndash; Returns current player state (home/browsing/playing)</li>
  2179. <li><b>playerStreamFavorite</b> &ndash; Shows if audio stream is a favorite (yes/no)</li>
  2180. <li><b>playerStreamRating</b> &ndash; Shows rating of the audio stream</li>
  2181. <li><b>playerTitle</b> &ndash; Returns audio stream's title</li>
  2182. <li><b>playerTotalTime</b> &ndash; Shows audio stream's total time</li>
  2183. <li><b>presence</b> &ndash; Returns peresence status (present/absent)</li>
  2184. <li><b>state</b> &ndash; Returns current state (on/off)</li>
  2185. <li><b>totalFavorites</b> &ndash; Returns total number of stored favorites (see getFavorites)</li>
  2186. <li><b>totalPresets</b> &ndash; Returns total number of stored presets (see getPresets)</li>
  2187. <li><b>volume</b> &ndash; Returns current relative volume (0...100%)</li>
  2188. <li><b>volumeStraight</b> &ndash; Returns current device absolute volume (0...64)</li>
  2189. </ul>
  2190. </ul>
  2191. <br>
  2192. </ul>
  2193. <a name="PHILIPS_AUDIOattr"></a>
  2194. <b>Attributes</b><br><br>
  2195. <ul>
  2196. <ul>
  2197. <li><b>autoGetFavorites</b> &ndash; Automatically read favorites from device if none available (default off)</li>
  2198. <li><b>autoGetPresets</b> &ndash; Automatically read presets from device if none available (default off)</li>
  2199. <li><b>do_not_notify</b></li>
  2200. <li><b>httpBufferTimeout</b> &ndash; Optional attribute defing the internal http buffer timeount (default 10)</li>
  2201. <li><b>maxListItems</b> &ndash; Defines max. number of player list items (default 100)</li>
  2202. <li><b>playerBrowsingTimeout</b> &ndash; Defines the inactivity timeout for browsing. After that timeout the player returns to the state 'home' in which the readings are updated automaically again. (default 180 seconds)</li>
  2203. <li><b>readingFnAttributes</b></li>
  2204. <li><b>requestTimeout</b> &ndash; Optional attribute defining the http response timeout (default 4 seconds)</li>
  2205. </ul>
  2206. </ul>
  2207. <br>
  2208. </ul>
  2209. =end html
  2210. =begin html_DE
  2211. <a name="PHILIPS_AUDIO"></a>
  2212. <h3>PHILIPS_AUDIO</h3>
  2213. <ul>
  2214. <a name="PHILIPS_AUDIOdefine"></a>
  2215. <b>Define</b><br><br>
  2216. <ul>
  2217. <code>
  2218. define &lt;name&gt; PHILIPS_AUDIO &lt;device model&gt; &lt;ip-address&gt; [&lt;status_interval&gt;]<br><br>
  2219. define &lt;name&gt; PHILIPS_AUDIO &lt;device model&gt; [&lt;off_status_interval&gt;] [&lt;on_status_interval&gt;]
  2220. </code>
  2221. <br><br>
  2222. Mit Hilfe dieses Moduls lassen sich Philips Audio Netzwerk Player wie z.B. MCi, Streamium oder Fidelio im lokalen Netzwerk steuern.<br>
  2223. Ger&auml;te, die &uuml;ber die myRemote App oder einen internen HTTP Server am Port 8889 sollten theoretisch ebenfalls bedient werden k&ouml;nnen.<br>
  2224. (http://[ip Nummer des Ger&auml;tes]:8889/index)<br>
  2225. <br>
  2226. (Getestet mit: AW9000, NP3500, NP3700 und NP3900)
  2227. <br><br>
  2228. Beispiel:<br>
  2229. <ul><br>
  2230. <code>
  2231. define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15
  2232. <br>
  2233. <br>
  2234. # 60 Sekunden Intervall<br>
  2235. define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b>
  2236. <br>
  2237. <br>
  2238. # 60 Sekunden Intervall f&uuml;r "off" und 10 Sekunden f&uuml;r "on"<br>
  2239. define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60 10</b>
  2240. </code>
  2241. <br><br>
  2242. <i>Bemerkung: Aufgrund der relativ langsamen Verarbeitung von Befehlen durch den Player selbst wurde das minimale Intervall <b>auf 5 Sekunden limitiert</b>. Dadurch sollten potentielle Ger&auml;tefreezes reduziert werden.</i>
  2243. </ul>
  2244. </ul><br>
  2245. <a name="PHILIPS_AUDIOset"></a>
  2246. <b>Set</b>
  2247. <ul>
  2248. <code>
  2249. set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]
  2250. </code>
  2251. <br><br>
  2252. <i>Bemerkung: Befehle und Parameter sind case-sensitive</i><br>
  2253. <ul><br>
  2254. <li><b>favoriteAdd</b> &ndash; F&uuml;gt den aktuellen Audiostream zu Favoriten hinzu</li>
  2255. <li><b>favoriteRemove</b> &ndash; L&ouml;scht den aktuellen Audiostream aus den Favoriten</li>
  2256. <li><b>getFavorites</b> &ndash; Liest aus die gespeicherten Favoriten aus dem Ger&auml;t (kann einige Zeit dauern...)</li>
  2257. <li><b>getMediaRendererDesc</b> &ndash; Liest aus Ger&auml;tspezifische Informationen aus (siehe auch deviceInfo reading)</li>
  2258. <li><b>getPresets</b> &ndash; Liest aus die gespeicherten Presets aus dem Ger&auml;t (kann einige Zeit dauern...)</li>
  2259. <li><b>input</b> &ndash; Schaltet auf den folgenden Eingang</li>
  2260. <ul>
  2261. <li><b>analogAux</b> &ndash; AUX input (nur AW9000)</li>
  2262. <li><b>digital1Coaxial</b> &ndash; digital coaxial input (nur AW9000)</li>
  2263. <li><b>digital2Optical</b> &ndash; digital optical input (nur AW9000)</li>
  2264. <li><b>internetRadio</b> &ndash; Internet Radio</li>
  2265. <li><b>mediaLibrary</b> &ndash; Media Library (UPnP/DLNA server) (nicht verf&uuml;gbar beim AW9000)</li>
  2266. <li><b>mp3Link</b> &ndash; Analoger MP3 Link (nicht verf&uuml;gbar beim AW9000)</li>
  2267. <li><b>onlineServices</b> &ndash; Online Services</li>
  2268. </ul>
  2269. <li><b>mute [ on | off ]</b> &ndash; Stummschaltung (an/aus)</li>
  2270. <li><b>player</b> &ndash; Player-Befehle</li>
  2271. <ul>
  2272. <li><b>next</b> &ndash; N&auml;chstee Audiostream</li>
  2273. <li><b>prev</b> &ndash; Letzter Audiostream</li>
  2274. <li><b>play-pause</b> &ndash; Play/pause des aktuellen Audiostreams</li>
  2275. <li><b>stop</b> &ndash; Stoppt das Abspielen des aktuellen Audiostreams</li>
  2276. </ul>
  2277. <li><b>repeat [ single | all | off]</b> &ndash; Stellt den repeat mode ein</li>
  2278. <li><b>selectFavorite [ name ]</b> &ndash; W&auml;hlt einen Favoriten. Leer falls keine Favoriten vorhanden (s. getFavorites)</li>
  2279. <li><b>selectFavoriteByNumber [ number ]</b> &ndash; W&auml;hlt einen Favoriten anhand seiner Speichernummer. Leer falls keine Favoriten vorhanden (s. getFavorites)</li>
  2280. <li><b>selectPreset [ name ]</b> &ndash; W&auml;hlt einen Preset. Leer falls keine Presets vorhanden (s. getPresets)</li>
  2281. <li><b>selectPresetByNumber [ number ]</b> &ndash; W&auml;hlt einen Preset anhand seiner Speichernummer. Leer falls keine Presets vorhanden (see also getPresets)</li>
  2282. <li><b>selectStream [ name ]</b> &ndash; Context-sensitive. W&auml;hlt einen Audiostream. H&auml;ngt vom aktuellen Inhalt der Playerlist ab. Ein 'c'-Pr&auml;fix repr&auml;sentiert einen 'Container' (Directory). ein 'i'-Pr&auml;fix repr&auml;sentiert ein 'Item' (audio stream).</li></li>
  2283. <li><b>shuffle [ on | off ]</b> &ndash; W&auml;hlt den gew&uuml;nschten Shuffle Modus</li>
  2284. <li><b>standbyButton</b> &ndash; Emuliert den standby-Knopf. Toggelt zwischen standby und power on</li>
  2285. <li><b>volume</b> &ndash; Setzt die relative Lautst&auml;rke 0...100%</li>
  2286. <li><b>volumeDown</b> &ndash; Setzt die Lautst&auml;rke um ein Dekrement herunter</li>
  2287. <li><b>volumeStraight</b> &ndash; Setzt die devicespezifische Lautst&auml;rke 0...64</li>
  2288. <li><b>volumeUp</b> &ndash; Setzt die Lautst&auml;rke um ein Inkrement herauf</li>
  2289. </ul>
  2290. </ul>
  2291. <br>
  2292. <a name="PHILIPS_AUDIOget"></a>
  2293. <b>Get</b>
  2294. <ul>
  2295. <code>
  2296. get &lt;name&gt; &lt;reading&gt &lt;reading name&gt;
  2297. </code>
  2298. <ul>
  2299. <br>
  2300. <li><b>deviceInfo</b> &ndash; Liefert devicespezifische Information</li>
  2301. <li><b>reading</b></li>
  2302. <ul>
  2303. <li><b>input</b> &ndash; Liefert den aktuellen Eingang oder '-' falls kein Audiostream aktiv</li>
  2304. <li><b>listItem_xxx</b> &ndash; Liefert Eintr&auml;ge der Playerliste (limitiert auf 999 Eintr&auml;ge)</li>
  2305. <li><b>networkError</b> &ndash; Liefert einen potentiellen Netzwerkfehler</li>
  2306. <li><b>networkRequest</b> &ndash; Liefert die aktuelle Netzwerkaktivit&auml;t (idle/busy)</li>
  2307. <li><b>power</b> &ndash; Liefert den Power-Status (on/off)</li>
  2308. <li><b>playerAlbum</b> &ndash; Liefert den Albumnamen des aktiven Audiostreams</li>
  2309. <li><b>playerAlbumArt</b> &ndash; Liefert die Albumart des aktiven Audiostreams</li>
  2310. <li><b>playerListStatus</b> &ndash; Liefert den aktuellen Zusatand der Playlist (busy/ready)</li>
  2311. <li><b>playerListTotalCount</b> &ndash; Liefert die Anzahl der Playlisteintr&auml;ge</li>
  2312. <li><b>playerPlayTime</b> &ndash; Liefert die aktuell Audiostreamspieldauer</li>
  2313. <li><b>playerPlaying</b> &ndash; Zeigt an, ob Audiostream abgespielt wird (yes/no)</li>
  2314. <li><b>playerRadioStation</b> &ndash; Liefert den Stationsnamen des Radiosenders</li>
  2315. <li><b>playerRadioStationInfo</b> &ndash; Liefert zus&auml;tzliche Informationen des Radiosenders</li>
  2316. <li><b>playerRepeat</b> &ndash; Zeigt den Reapeat Mode an (off/single/all)</li>
  2317. <li><b>playerShuffle</b> &ndash; Zeigt den aktuellen Shuffle mode an (on/off)</li>
  2318. <li><b>playerState</b> &ndash; Zeigt den Playerzustand an (home/browsing/playing)</li>
  2319. <li><b>playerStreamFavorite</b> &ndash; Zeigt an, ob aktueller Audiostream ein Favorit ist (yes/no)</li>
  2320. <li><b>playerStreamRating</b> &ndash; Zeigt das rating des Audiostreams</li>
  2321. <li><b>playerTitle</b> &ndash; Zeigt den Titel des Audiostreams an</li>
  2322. <li><b>playerTotalTime</b> &ndash; Zeigt die Audiostreamdauer an</li>
  2323. <li><b>presence</b> &ndash; Liefert den presence status (present/absent)</li>
  2324. <li><b>state</b> &ndash; Lifert den aktuellen Ger&auml;testatus (on/off)</li>
  2325. <li><b>totalFavorites</b> &ndash; Liefert die Anzahl gepseicherter Favoriten (s. getFavorites)</li>
  2326. <li><b>totalPresets</b> &ndash; Liefert die Anzahl gepseicherter Presets (see getPresets)</li>
  2327. <li><b>volume</b> &ndash; Liefert die relative Lutst&auml;rke (0...100%)</li>
  2328. <li><b>volumeStraight</b> &ndash; Liefert die devicespezifische Lautst&auml;rke (0...64)</li>
  2329. </ul>
  2330. </ul>
  2331. <br>
  2332. </ul>
  2333. <a name="PHILIPS_AUDIOattr"></a>
  2334. <b>Attribute</b><br><br>
  2335. <ul>
  2336. <ul>
  2337. <li><b>autoGetFavorites</b> &ndash; Automatisches Auslesen der Favoriten beim Modulstart falls keine vorhanden (default off)</li>
  2338. <li><b>autoGetPresets</b> &ndash; Automatisches Auslesen der Presets beim Modulstart falls keine vorhanden (default off)</li>
  2339. <li><b>do_not_notify</b></li>
  2340. <li><b>httpBufferTimeout</b> &ndash; Optionalles Attribut f&uuml;r den internen http buffer timeount (default 10 Sekunden)</li>
  2341. <li><b>maxListItems</b> &ndash; Definiert die max. Anzahl der anzuzeigenden Playerlisteintr&auml;ge (default 100)</li>
  2342. <li><b>playerBrowsingTimeout</b> &ndash; Definiert den Inaktivit&auml;ts-Timeout beim Browsen der Playerlist. Nach diesem Timeout f&auml;llt das Modul in den "home"-State zur&uuml;ck. Die Playerreadings werden wieder aktualisiert (default 180 Sekunden)</li>
  2343. <li><b>readingFnAttributes</b></li>
  2344. <li><b>requestTimeout</b> &ndash; Optionalles Attribut f&uuml;r http responses (default 4 Sekunden)</li>
  2345. </ul>
  2346. </ul>
  2347. </ul>
  2348. =end html_DE
  2349. =cut