70_ONKYO_AVR.pm 121 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504
  1. ###############################################################################
  2. # $Id: 70_ONKYO_AVR.pm 14399 2017-05-28 10:33:19Z loredo $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use Data::Dumper;
  7. use Symbol qw<qualify_to_ref>;
  8. use File::Path;
  9. use File::stat;
  10. use File::Temp;
  11. use File::Copy;
  12. # initialize ##################################################################
  13. sub ONKYO_AVR_Initialize($) {
  14. my ($hash) = @_;
  15. Log3 $hash, 5, "ONKYO_AVR_Initialize: Entering";
  16. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  17. require "$attr{global}{modpath}/FHEM/ONKYOdb.pm";
  18. $hash->{DefFn} = "ONKYO_AVR_Define";
  19. $hash->{UndefFn} = "ONKYO_AVR_Undefine";
  20. $hash->{SetFn} = "ONKYO_AVR_Set";
  21. $hash->{GetFn} = "ONKYO_AVR_Get";
  22. $hash->{ReadFn} = "ONKYO_AVR_Read";
  23. $hash->{WriteFn} = "ONKYO_AVR_Write";
  24. $hash->{ReadyFn} = "ONKYO_AVR_Ready";
  25. $hash->{NotifyFn} = "ONKYO_AVR_Notify";
  26. $hash->{ShutdownFn} = "ONKYO_AVR_Shutdown";
  27. $hash->{parseParams} = 1;
  28. no warnings 'qw';
  29. my @attrList = qw(
  30. do_not_notify:1,0
  31. disabledForIntervals
  32. volumeSteps:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
  33. volumeMax:slider,0,1,100
  34. inputs
  35. disable:0,1
  36. model
  37. wakeupCmd:textField
  38. connectionCheck:off,30,45,60,75,90,105,120
  39. timeout:1,2,3,4,5
  40. );
  41. use warnings 'qw';
  42. $hash->{AttrList} = join( " ", @attrList ) . " " . $readingFnAttributes;
  43. $data{RC_layout}{ONKYO_AVR_SVG} = "ONKYO_AVR_RClayout_SVG";
  44. $data{RC_layout}{ONKYO_AVR} = "ONKYO_AVR_RClayout";
  45. $data{RC_makenotify}{ONKYO_AVR} = "ONKYO_AVR_RCmakenotify";
  46. # 98_powerMap.pm support
  47. $hash->{powerMap} = {
  48. model => {
  49. 'TX-NR626' => {
  50. rname_E => 'energy',
  51. rname_P => 'consumption',
  52. map => {
  53. stateAV => {
  54. absent => 0,
  55. off => 0,
  56. muted => 85,
  57. '*' => 140,
  58. },
  59. },
  60. },
  61. },
  62. };
  63. }
  64. # regular Fn ##################################################################
  65. sub ONKYO_AVR_Define($$$) {
  66. my ( $hash, $a, $h ) = @_;
  67. my $name = $hash->{NAME};
  68. my $infix = "ONKYO_AVR";
  69. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Define()";
  70. eval { require XML::Simple; };
  71. return "Please install Perl XML::Simple to use module ONKYO_AVR"
  72. if ($@);
  73. if ( int(@$a) < 3 ) {
  74. my $msg =
  75. "Wrong syntax: define <name> ONKYO_AVR { <ip-or-hostname[:port]> | <devicename[\@baudrate]> } [<protocol-version>]";
  76. Log3 $name, 4, $msg;
  77. return $msg;
  78. }
  79. RemoveInternalTimer($hash);
  80. DevIo_CloseDev($hash);
  81. delete $hash->{NEXT_OPEN} if ( defined( $hash->{NEXT_OPEN} ) );
  82. $hash->{Clients} = ":ONKYO_AVR_ZONE:";
  83. $hash->{TIMEOUT} = AttrVal( $name, "timeout", "3" );
  84. # used zone to control
  85. $hash->{ZONE} = "1";
  86. $hash->{INPUT} = "";
  87. $hash->{SCREENLAYER} = "0";
  88. # protocol version
  89. $hash->{PROTOCOLVERSION} = @$a[3] || 2013;
  90. if ( !( $hash->{PROTOCOLVERSION} =~ /^(2013|pre2013)$/ ) ) {
  91. return "Invalid protocol, choose one of 2013 pre2013";
  92. }
  93. if (
  94. $hash->{PROTOCOLVERSION} eq "pre2013"
  95. && ( !exists( $attr{$name}{model} )
  96. || $attr{$name}{model} ne $hash->{PROTOCOLVERSION} )
  97. )
  98. {
  99. $attr{$name}{model} = $hash->{PROTOCOLVERSION};
  100. }
  101. # set default settings on first define
  102. if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
  103. fhem 'attr ' . $name . ' stateFormat stateAV';
  104. fhem 'attr ' . $name
  105. . ' cmdIcon muteT:rc_MUTE previous:rc_PREVIOUS next:rc_NEXT play:rc_PLAY pause:rc_PAUSE stop:rc_STOP shuffleT:rc_SHUFFLE repeatT:rc_REPEAT';
  106. fhem 'attr ' . $name . ' webCmd volume:muteT:input:previous:next';
  107. fhem 'attr ' . $name
  108. . ' devStateIcon on:rc_GREEN@green:off off:rc_STOP:on absent:rc_RED playing:rc_PLAY@green:pause paused:rc_PAUSE@green:play muted:rc_MUTE@green:muteT fast-rewind:rc_REW@green:play fast-forward:rc_FF@green:play interrupted:rc_PAUSE@yellow:play';
  109. }
  110. $hash->{helper}{receiver}{device}{zonelist}{zone}{1}{name} = "Main";
  111. $hash->{helper}{receiver}{device}{zonelist}{zone}{1}{value} = "1";
  112. $modules{ONKYO_AVR_ZONE}{defptr}{$name}{1} = $hash;
  113. $hash->{DeviceName} = @$a[2];
  114. if ( ONKYO_AVR_addExtension( $name, "ONKYO_AVR_CGI", $infix ) ) {
  115. $hash->{fhem}{infix} = $infix;
  116. }
  117. # connect using serial connection (old blocking style)
  118. if ( $hash->{DeviceName} =~ m/^UNIX:(SEQPACKET|STREAM):(.*)$/
  119. || $hash->{DeviceName} =~ m/^FHEM:DEVIO:(.*)(:(.*))/ )
  120. {
  121. my $ret = DevIo_OpenDev( $hash, 0, "ONKYO_AVR_DevInit" );
  122. return $ret;
  123. }
  124. # connect using TCP connection (non-blocking style)
  125. else {
  126. # add missing port if required
  127. $hash->{DeviceName} = $hash->{DeviceName} . ":60128"
  128. if ( $hash->{DeviceName} !~ m/^(.+):([0-9]+)$/ );
  129. DevIo_OpenDev(
  130. $hash, 0,
  131. "ONKYO_AVR_DevInit",
  132. sub() {
  133. my ( $hash, $err ) = @_;
  134. Log3 $name, 4, "ONKYO_AVR $name: $err" if ($err);
  135. }
  136. );
  137. }
  138. return undef;
  139. }
  140. sub ONKYO_AVR_Undefine($$) {
  141. my ( $hash, $name ) = @_;
  142. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Undefine()";
  143. if ( defined( $hash->{fhem}{infix} ) ) {
  144. ONKYO_AVR_removeExtension( $hash->{fhem}{infix} );
  145. }
  146. RemoveInternalTimer($hash);
  147. foreach my $d ( sort keys %defs ) {
  148. if ( defined( $defs{$d} )
  149. && defined( $defs{$d}{IODev} )
  150. && $defs{$d}{IODev} == $hash )
  151. {
  152. my $lev = ( $reread_active ? 4 : 2 );
  153. Log3 $name, $lev, "deleting port for $d";
  154. delete $defs{$d}{IODev};
  155. }
  156. }
  157. DevIo_CloseDev($hash);
  158. return undef;
  159. }
  160. sub ONKYO_AVR_Set($$$) {
  161. my ( $hash, $a, $h ) = @_;
  162. my $name = $hash->{NAME};
  163. my $zone = $hash->{ZONE};
  164. my $state = ReadingsVal( $name, "power", "off" );
  165. my $presence = ReadingsVal( $name, "presence", "absent" );
  166. my $return;
  167. my $reading;
  168. my $inputs_txt = "";
  169. my $channels_txt = "";
  170. my @implicit_cmds;
  171. my $implicit_txt = "";
  172. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Set()";
  173. return "Argument is missing" if ( int(@$a) < 1 );
  174. # Input alias handling
  175. if ( defined( $attr{$name}{inputs} ) && $attr{$name}{inputs} ne "" ) {
  176. my @inputs = split( ':', $attr{$name}{inputs} );
  177. if (@inputs) {
  178. foreach (@inputs) {
  179. if (m/[^,\s]+(,[^,\s]+)+/) {
  180. my @input_names = split( ',', $_ );
  181. $inputs_txt .= $input_names[1] . ",";
  182. $input_names[1] =~ s/\s/_/g;
  183. $hash->{helper}{receiver}{input_aliases}{ $input_names[0] }
  184. = $input_names[1];
  185. $hash->{helper}{receiver}{input_names}{ $input_names[1] } =
  186. $input_names[0];
  187. }
  188. else {
  189. $inputs_txt .= $_ . ",";
  190. }
  191. }
  192. }
  193. $inputs_txt =~ s/\s/_/g;
  194. $inputs_txt = substr( $inputs_txt, 0, -1 );
  195. }
  196. # if we could read the actual available inputs from the receiver, use them
  197. elsif (defined( $hash->{helper}{receiver} )
  198. && ref( $hash->{helper}{receiver} ) eq "HASH"
  199. && defined( $hash->{helper}{receiver}{device}{selectorlist}{count} )
  200. && $hash->{helper}{receiver}{device}{selectorlist}{count} > 0 )
  201. {
  202. foreach my $input (
  203. @{ $hash->{helper}{receiver}{device}{selectorlist}{selector} } )
  204. {
  205. if ( $input->{value} eq "1"
  206. && $input->{zone} ne "00"
  207. && $input->{id} ne "80" )
  208. {
  209. my $id = $input->{id};
  210. my $name = trim( $input->{name} );
  211. $inputs_txt .= $name . ",";
  212. }
  213. }
  214. $inputs_txt =~ s/\s/_/g;
  215. $inputs_txt = substr( $inputs_txt, 0, -1 );
  216. }
  217. # use general list of possible inputs
  218. else {
  219. # Find out valid inputs
  220. my $inputs =
  221. ONKYOdb::ONKYO_GetRemotecontrolValue( $zone,
  222. ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, "input" ) );
  223. foreach my $input ( sort keys %{$inputs} ) {
  224. $inputs_txt .= $input . ","
  225. if ( !( $input =~ /^(07|08|09|up|down|query)$/ ) );
  226. }
  227. $inputs_txt = substr( $inputs_txt, 0, -1 );
  228. }
  229. # list of network channels/services
  230. my $channels_src = "internal";
  231. if ( defined( $hash->{helper}{receiver} )
  232. && ref( $hash->{helper}{receiver} ) eq "HASH"
  233. && defined( $hash->{helper}{receiver}{device}{netservicelist}{count} )
  234. && $hash->{helper}{receiver}{device}{netservicelist}{count} > 0 )
  235. {
  236. foreach my $id (
  237. sort keys
  238. %{ $hash->{helper}{receiver}{device}{netservicelist}{netservice} } )
  239. {
  240. if (
  241. defined(
  242. $hash->{helper}{receiver}{device}{netservicelist}
  243. {netservice}{$id}{value}
  244. )
  245. && $hash->{helper}{receiver}{device}{netservicelist}
  246. {netservice}{$id}{value} eq "1"
  247. )
  248. {
  249. $channels_txt .=
  250. trim( $hash->{helper}{receiver}{device}{netservicelist}
  251. {netservice}{$id}{name} )
  252. . ",";
  253. }
  254. }
  255. $channels_txt =~ s/\s/_/g;
  256. $channels_txt = substr( $channels_txt, 0, -1 );
  257. $channels_src = "receiver";
  258. }
  259. # use general list of possible channels
  260. else {
  261. # Find out valid channels
  262. my $channels =
  263. ONKYOdb::ONKYO_GetRemotecontrolValue( "1",
  264. ONKYOdb::ONKYO_GetRemotecontrolCommand( "1", "net-service" ) );
  265. foreach my $channel ( sort keys %{$channels} ) {
  266. $channels_txt .= $channel . ","
  267. if ( !( $channel =~ /^(up|down|query)$/ ) );
  268. }
  269. $channels_txt = substr( $channels_txt, 0, -1 );
  270. }
  271. # for each reading, check if there is a known command for it
  272. # and allow to set values if there are any available
  273. if ( defined( $hash->{READINGS} ) ) {
  274. foreach my $reading ( keys %{ $hash->{READINGS} } ) {
  275. my $cmd_raw =
  276. ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, $reading );
  277. my @readingExceptions = (
  278. "volume", "input", "mute", "sleep", "center-temporary-level",
  279. "subwoofer-temporary-level", "balance", "preset",
  280. );
  281. if ( $cmd_raw && !( grep $_ eq $reading, @readingExceptions ) ) {
  282. my $cmd_details =
  283. ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone,
  284. $cmd_raw );
  285. my $value_list = "";
  286. my $debuglist;
  287. foreach my $value ( keys %{ $cmd_details->{values} } ) {
  288. next
  289. if ( $value eq "QSTN" );
  290. if ( defined( $cmd_details->{values}{$value}{name} ) ) {
  291. $value_list .= "," if ( $value_list ne "" );
  292. $value_list .= $cmd_details->{values}{$value}{name}
  293. if (
  294. ref( $cmd_details->{values}{$value}{name} ) eq "" );
  295. $value_list .= $cmd_details->{values}{$value}{name}[0]
  296. if (
  297. ref( $cmd_details->{values}{$value}{name} ) eq
  298. "ARRAY" );
  299. }
  300. }
  301. if ( $value_list ne "" ) {
  302. push @implicit_cmds, $reading;
  303. $implicit_txt .= " $reading:$value_list";
  304. }
  305. }
  306. # tone-*
  307. elsif ( $reading =~ /^tone.*-([a-zA-Z]+)$/ ) {
  308. $implicit_txt .= " $reading:slider,-10,1,10";
  309. }
  310. # center-temporary-level
  311. elsif ( $reading eq "center-temporary-level" ) {
  312. $implicit_txt .= " $reading:slider,-12,1,12";
  313. }
  314. # subwoofer*-temporary-level
  315. elsif ( $reading =~ /^subwoofer.*-temporary-level$/ ) {
  316. $implicit_txt .= " $reading:slider,-15,1,12";
  317. }
  318. }
  319. }
  320. my $preset_txt = "";
  321. if ( defined( $hash->{helper}{receiver}{preset} ) ) {
  322. foreach my $id (
  323. sort
  324. keys %{ $hash->{helper}{receiver}{preset} }
  325. )
  326. {
  327. my $presetName =
  328. $hash->{helper}{receiver}{preset}{$id};
  329. next if ( !$presetName || $presetName eq "" );
  330. $preset_txt = "preset:" if ( $preset_txt eq "" );
  331. $preset_txt .= ","
  332. if ( $preset_txt eq "preset:"
  333. && ReadingsVal( $name, "preset", "-" ) eq "" );
  334. $presetName =~ s/\s/_/g;
  335. $preset_txt .= $presetName . ",";
  336. }
  337. }
  338. $preset_txt = substr( $preset_txt, 0, -1 ) if ( $preset_txt ne "" );
  339. if ( $preset_txt eq "" ) {
  340. $preset_txt = "preset:";
  341. $preset_txt .= "," if ( ReadingsVal( $name, "preset", "-" ) eq "" );
  342. $preset_txt .=
  343. "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40";
  344. }
  345. my $shuffle_txt = "shuffle:";
  346. $shuffle_txt .= "," if ( ReadingsVal( $name, "shuffle", "-" ) eq "-" );
  347. $shuffle_txt .= "off,on,on-album,on-folder";
  348. my $repeat_txt = "repeat:";
  349. $repeat_txt .= "," if ( ReadingsVal( $name, "repeat", "-" ) eq "-" );
  350. $repeat_txt .= "off,all,all-folder,one";
  351. my $usage =
  352. "Unknown argument '"
  353. . @$a[1]
  354. . "', choose one of toggle:noArg on:noArg off:noArg volume:slider,0,1,100 volumeDown:noArg volumeUp:noArg mute:off,on muteT:noArg play:noArg pause:noArg stop:noArg previous:noArg next:noArg shuffleT:noArg repeatT:noArg remoteControl:play,pause,repeat,stop,top,down,up,right,delete,display,ff,left,mode,return,rew,select,setup,0,1,2,3,4,5,6,7,8,9,prev,next,shuffle,menu channelDown:noArg channelUp:noArg inputDown:noArg inputUp:noArg internet-radio-preset:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 input:"
  355. . $inputs_txt;
  356. $usage .= " channel:$channels_txt";
  357. $usage .= " presetDown:noArg presetUp:noArg $preset_txt";
  358. $usage .= " $shuffle_txt";
  359. $usage .= " $repeat_txt";
  360. $usage .= $implicit_txt if ( $implicit_txt ne "" );
  361. $usage .= " sleep:off,5,10,15,30,60,90";
  362. if ( ReadingsVal( $name, "currentTrackPosition", "--:--" ) ne "--:--" ) {
  363. $usage .= " currentTrackPosition";
  364. }
  365. my $cmd = '';
  366. return "Device is offline and cannot be controlled at that stage."
  367. if ( $presence eq "absent"
  368. && lc( @$a[1] ) ne "on"
  369. && lc( @$a[1] ) ne "?"
  370. && lc( @$a[1] ) ne "help" );
  371. readingsBeginUpdate($hash);
  372. # create inputList reading for frontends
  373. readingsBulkUpdate( $hash, "inputList", $inputs_txt )
  374. if ( ReadingsVal( $name, "inputList", "-" ) ne $inputs_txt );
  375. # create channelList reading for frontends
  376. readingsBulkUpdate( $hash, "channelList", $channels_txt )
  377. if (
  378. (
  379. $channels_src eq "internal"
  380. && ReadingsVal( $name, "channelList", "-" ) eq "-"
  381. )
  382. || ( $channels_src eq "receiver"
  383. && ReadingsVal( $name, "channelList", "-" ) ne $channels_txt )
  384. );
  385. # channel
  386. if ( lc( @$a[1] ) eq "channel" ) {
  387. if ( !defined( @$a[2] ) ) {
  388. $return = "Syntax: CHANNELNAME [USERNAME PASSWORD]";
  389. }
  390. else {
  391. if ( $state eq "off" ) {
  392. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  393. my $ret = fhem "sleep 5;set $name channel " . @$a[2];
  394. $return .= $ret if ($ret);
  395. }
  396. elsif ( $hash->{INPUT} ne "2B" ) {
  397. $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" );
  398. my $ret = fhem "sleep 1;set $name channel " . @$a[2];
  399. $return .= $ret if ($ret);
  400. }
  401. elsif ( ReadingsVal( $name, "channel", "" ) ne @$a[2]
  402. || ( defined( @$a[3] ) && defined( @$a[4] ) ) )
  403. {
  404. my $servicename = "";
  405. my $channelname = @$a[2];
  406. if (
  407. defined( $hash->{helper}{receiver} )
  408. && ref( $hash->{helper}{receiver} ) eq "HASH"
  409. && defined(
  410. $hash->{helper}{receiver}{device}{netservicelist}{count}
  411. )
  412. && $hash->{helper}{receiver}{device}{netservicelist}{count}
  413. > 0
  414. )
  415. {
  416. $channelname =~ s/_/ /g;
  417. foreach my $id (
  418. sort keys %{
  419. $hash->{helper}{receiver}{device}{netservicelist}
  420. {netservice}
  421. }
  422. )
  423. {
  424. if (
  425. defined(
  426. $hash->{helper}{receiver}{device}
  427. {netservicelist}{netservice}{$id}{value}
  428. )
  429. && $hash->{helper}{receiver}{device}
  430. {netservicelist}{netservice}{$id}{value} eq "1"
  431. && $hash->{helper}{receiver}{device}
  432. {netservicelist}{netservice}{$id}{name} eq
  433. $channelname
  434. )
  435. {
  436. $servicename .= uc($id);
  437. last;
  438. }
  439. }
  440. }
  441. else {
  442. my $channels = ONKYOdb::ONKYO_GetRemotecontrolValue(
  443. "1",
  444. ONKYOdb::ONKYO_GetRemotecontrolCommand(
  445. "1", "net-service"
  446. )
  447. );
  448. $servicename = $channels->{$channelname}
  449. if ( defined( $channels->{$channelname} ) );
  450. }
  451. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  452. $servicename = uc($channelname)
  453. if ( $servicename eq "" );
  454. $servicename .= "0" if ( !defined( @$a[3] ) );
  455. $servicename .= "1" . @$a[3] if ( defined( @$a[3] ) );
  456. $servicename .= @$a[4] if ( defined( @$a[4] ) );
  457. $return =
  458. ONKYO_AVR_SendCommand( $hash, "net-service", $servicename );
  459. }
  460. }
  461. }
  462. # channelDown
  463. elsif ( lc( @$a[1] ) eq "channeldown" ) {
  464. if ( $state eq "off" ) {
  465. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  466. my $ret = fhem "sleep 5;set $name channelDown";
  467. $return .= $ret if ($ret);
  468. }
  469. elsif ( $hash->{INPUT} ne "2B" ) {
  470. $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" );
  471. my $ret = fhem "sleep 1;set $name channelDown";
  472. $return .= $ret if ($ret);
  473. }
  474. else {
  475. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  476. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chdn" );
  477. }
  478. }
  479. # channelUp
  480. elsif ( lc( @$a[1] ) eq "channelup" ) {
  481. if ( $state eq "off" ) {
  482. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  483. my $ret = fhem "sleep 5;set $name channelUp";
  484. $return .= $ret if ($ret);
  485. }
  486. elsif ( $hash->{INPUT} ne "2B" ) {
  487. $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" );
  488. my $ret = fhem "sleep 1;set $name channelUp";
  489. $return .= $ret if ($ret);
  490. }
  491. else {
  492. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  493. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chup" );
  494. }
  495. }
  496. # currentTrackPosition
  497. elsif ( lc( @$a[1] ) eq "currenttrackposition" ) {
  498. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  499. if ( !defined( @$a[2] ) ) {
  500. $return = "No argument given";
  501. }
  502. else {
  503. if ( @$a[2] !~ /^[0-9][0-9]:[0-5][0-9]$/ ) {
  504. $return =
  505. "Time needs to have format mm:ss and between 00:00 and 99:59";
  506. }
  507. else {
  508. $return =
  509. ONKYO_AVR_SendCommand( $hash, "net-usb-time-seek", @$a[2] );
  510. }
  511. }
  512. }
  513. # internet-radio-preset
  514. elsif ( lc( @$a[1] ) eq "internet-radio-preset" ) {
  515. if ( !defined( @$a[2] ) ) {
  516. $return = "No argument given";
  517. }
  518. else {
  519. if ( $state eq "off" ) {
  520. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  521. my $ret = fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2];
  522. $return .= $ret if ($ret);
  523. }
  524. elsif ( $hash->{INPUT} ne "2B" ) {
  525. $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" );
  526. my $ret = fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2];
  527. $return .= $ret if ($ret);
  528. }
  529. elsif ( @$a[2] =~ /^\d*$/ ) {
  530. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  531. $return = ONKYO_AVR_SendCommand(
  532. $hash,
  533. lc( @$a[1] ),
  534. ONKYO_AVR_dec2hex( @$a[2] )
  535. );
  536. }
  537. else {
  538. $return = "Invalid argument format";
  539. }
  540. }
  541. }
  542. # preset
  543. elsif ( lc( @$a[1] ) eq "preset" ) {
  544. if ( !defined( @$a[2] ) ) {
  545. $return = "No argument given";
  546. }
  547. else {
  548. if ( $state eq "off" ) {
  549. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  550. my $ret = fhem "sleep 5;set $name preset " . @$a[2];
  551. $return .= $ret if ($ret);
  552. }
  553. elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) {
  554. $return = ONKYO_AVR_SendCommand( $hash, "input", "24" );
  555. my $ret = fhem "sleep 1;set $name preset " . @$a[2];
  556. $return .= $ret if ($ret);
  557. }
  558. elsif ( lc( @$a[2] ) eq "up" ) {
  559. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  560. $return = ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "UP" );
  561. }
  562. elsif ( lc( @$a[2] ) eq "down" ) {
  563. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  564. $return =
  565. ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "DOWN" );
  566. }
  567. elsif ( @$a[2] =~ /^\d*$/ ) {
  568. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  569. $return = ONKYO_AVR_SendCommand(
  570. $hash,
  571. lc( @$a[1] ),
  572. ONKYO_AVR_dec2hex( @$a[2] )
  573. );
  574. }
  575. elsif ( defined( $hash->{helper}{receiver}{preset} ) ) {
  576. foreach
  577. my $id ( sort keys %{ $hash->{helper}{receiver}{preset} } )
  578. {
  579. my $presetName =
  580. $hash->{helper}{receiver}{preset}{$id};
  581. next if ( !$presetName || $presetName eq "" );
  582. $presetName =~ s/\s/_/g;
  583. if ( $presetName eq @$a[2] ) {
  584. Log3 $name, 3,
  585. "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  586. $return =
  587. ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), uc($id) );
  588. last;
  589. }
  590. }
  591. }
  592. }
  593. }
  594. # presetDown
  595. elsif ( lc( @$a[1] ) eq "presetdown" ) {
  596. if ( $state eq "off" ) {
  597. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  598. my $ret = fhem "sleep 5;set $name presetDown";
  599. $return .= $ret if ($ret);
  600. }
  601. elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) {
  602. $return = ONKYO_AVR_SendCommand( $hash, "input", "24" );
  603. my $ret = fhem "sleep 1;set $name presetDown";
  604. $return .= $ret if ($ret);
  605. }
  606. else {
  607. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  608. $return = ONKYO_AVR_SendCommand( $hash, "preset", "down" );
  609. }
  610. }
  611. # presetUp
  612. elsif ( lc( @$a[1] ) eq "presetup" ) {
  613. if ( $state eq "off" ) {
  614. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  615. my $ret = fhem "sleep 5;set $name presetUp";
  616. $return .= $ret if ($ret);
  617. }
  618. elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) {
  619. $return = ONKYO_AVR_SendCommand( $hash, "input", "24" );
  620. my $ret = fhem "sleep 1;set $name presetUp";
  621. $return .= $ret if ($ret);
  622. }
  623. else {
  624. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  625. $return = ONKYO_AVR_SendCommand( $hash, "preset", "up" );
  626. }
  627. }
  628. # tone-*
  629. elsif ( lc( @$a[1] ) =~ /^(tone.*)-(bass|treble)$/ ) {
  630. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  631. if ( !defined( @$a[2] ) ) {
  632. $return = "No argument given";
  633. }
  634. else {
  635. if ( $state eq "off" ) {
  636. $return =
  637. "Device power is turned off, this function is unavailable at that stage.";
  638. }
  639. elsif ( lc( @$a[2] ) eq "up" ) {
  640. my $setVal = "";
  641. $setVal = "B" if ( $2 eq "bass" );
  642. $setVal = "T" if ( $2 eq "treble" );
  643. $return =
  644. ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "UP" );
  645. }
  646. elsif ( lc( @$a[2] ) eq "down" ) {
  647. my $setVal = "";
  648. $setVal = "B" if ( $2 eq "bass" );
  649. $setVal = "T" if ( $2 eq "treble" );
  650. $return =
  651. ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "DOWN" );
  652. }
  653. elsif ( @$a[2] =~ /^-*\d+$/ ) {
  654. my $setVal = "";
  655. $setVal = "B" if ( $2 eq "bass" );
  656. $setVal = "T" if ( $2 eq "treble" );
  657. $setVal .= "+" if ( @$a[2] > 0 );
  658. $setVal .= "-" if ( @$a[2] < 0 );
  659. my $setVal2 = @$a[2];
  660. $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 );
  661. $setVal2 = ONKYO_AVR_dec2hex($setVal2);
  662. $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" );
  663. $return =
  664. ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . $setVal2 );
  665. }
  666. }
  667. }
  668. # center-temporary-level
  669. # subwoofer-temporary-level
  670. elsif (lc( @$a[1] ) eq "center-temporary-level"
  671. || lc( @$a[1] ) eq "subwoofer-temporary-level" )
  672. {
  673. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  674. if ( !defined( @$a[2] ) ) {
  675. $return = "No argument given";
  676. }
  677. else {
  678. if ( $state eq "off" ) {
  679. $return =
  680. "Device power is turned off, this function is unavailable at that stage.";
  681. }
  682. elsif ( lc( @$a[2] ) eq "up" ) {
  683. $return = ONKYO_AVR_SendCommand( $hash, lc($1), "UP" );
  684. }
  685. elsif ( lc( @$a[2] ) eq "down" ) {
  686. $return = ONKYO_AVR_SendCommand( $hash, lc($1), "DOWN" );
  687. }
  688. elsif ( @$a[2] =~ /^-*\d+$/ ) {
  689. my $setVal = "";
  690. $setVal = "+" if ( @$a[2] > 0 );
  691. $setVal = "-" if ( @$a[2] < 0 );
  692. my $setVal2 = @$a[2];
  693. $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 );
  694. $setVal2 = ONKYO_AVR_dec2hex($setVal2);
  695. $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" );
  696. $return = ONKYO_AVR_SendCommand(
  697. $hash,
  698. lc( @$a[1] ),
  699. $setVal . $setVal2
  700. );
  701. }
  702. }
  703. }
  704. # toggle
  705. elsif ( lc( @$a[1] ) eq "toggle" ) {
  706. if ( $state eq "off" ) {
  707. $return = fhem "set $name on";
  708. }
  709. else {
  710. $return = fhem "set $name off";
  711. }
  712. }
  713. # on
  714. elsif ( lc( @$a[1] ) eq "on" ) {
  715. if ( $presence eq "absent" ) {
  716. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " (wakeup)";
  717. my $wakeupCmd = AttrVal( $name, "wakeupCmd", "" );
  718. if ( $wakeupCmd ne "" ) {
  719. $wakeupCmd =~ s/\$DEVICE/$name/g;
  720. if ( $wakeupCmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) {
  721. Log3 $name, 4,
  722. "ONKYO_AVR executing wake-up command (Perl): $wakeupCmd";
  723. $return = eval $wakeupCmd;
  724. }
  725. else {
  726. Log3 $name, 4,
  727. "ONKYO_AVR executing wake-up command (fhem): $wakeupCmd";
  728. $return = fhem $wakeupCmd;
  729. }
  730. }
  731. else {
  732. $return =
  733. "Device is offline and cannot be controlled at that stage.";
  734. $return .=
  735. "\nYou may enable network-standby to allow a permanent connection to the device by the following command:\nget $name remoteControl network-standby on"
  736. if ( ReadingsVal( $name, "network-standby", "off" ) ne "on" );
  737. }
  738. }
  739. else {
  740. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  741. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  742. # don't wait for receiver to confirm power on
  743. #
  744. readingsBeginUpdate($hash);
  745. # power
  746. readingsBulkUpdate( $hash, "power", "on" )
  747. if ( ReadingsVal( $name, "power", "-" ) ne "on" );
  748. # stateAV
  749. my $stateAV = ONKYO_AVR_GetStateAV($hash);
  750. readingsBulkUpdate( $hash, "stateAV", $stateAV )
  751. if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
  752. readingsEndUpdate( $hash, 1 );
  753. }
  754. }
  755. # off
  756. elsif ( lc( @$a[1] ) eq "off" ) {
  757. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  758. $return = ONKYO_AVR_SendCommand( $hash, "power", "off" );
  759. }
  760. # remoteControl
  761. elsif ( lc( @$a[1] ) eq "remotecontrol" ) {
  762. if ( !defined( @$a[2] ) ) {
  763. $return = "No argument given, choose one of minutes off";
  764. }
  765. else {
  766. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  767. if ( lc( @$a[2] ) eq "play"
  768. || lc( @$a[2] ) eq "pause"
  769. || lc( @$a[2] ) eq "repeat"
  770. || lc( @$a[2] ) eq "stop"
  771. || lc( @$a[2] ) eq "top"
  772. || lc( @$a[2] ) eq "down"
  773. || lc( @$a[2] ) eq "up"
  774. || lc( @$a[2] ) eq "right"
  775. || lc( @$a[2] ) eq "delete"
  776. || lc( @$a[2] ) eq "display"
  777. || lc( @$a[2] ) eq "ff"
  778. || lc( @$a[2] ) eq "left"
  779. || lc( @$a[2] ) eq "mode"
  780. || lc( @$a[2] ) eq "return"
  781. || lc( @$a[2] ) eq "rew"
  782. || lc( @$a[2] ) eq "select"
  783. || lc( @$a[2] ) eq "setup"
  784. || lc( @$a[2] ) eq "0"
  785. || lc( @$a[2] ) eq "1"
  786. || lc( @$a[2] ) eq "2"
  787. || lc( @$a[2] ) eq "3"
  788. || lc( @$a[2] ) eq "4"
  789. || lc( @$a[2] ) eq "5"
  790. || lc( @$a[2] ) eq "6"
  791. || lc( @$a[2] ) eq "7"
  792. || lc( @$a[2] ) eq "8"
  793. || lc( @$a[2] ) eq "9" )
  794. {
  795. $return =
  796. ONKYO_AVR_SendCommand( $hash, "net-usb", lc( @$a[2] ) );
  797. }
  798. elsif ( lc( @$a[2] ) eq "prev" ) {
  799. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" );
  800. }
  801. elsif ( lc( @$a[2] ) eq "next" ) {
  802. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" );
  803. }
  804. elsif ( lc( @$a[2] ) eq "shuffle" ) {
  805. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" );
  806. }
  807. elsif ( lc( @$a[2] ) eq "menu" ) {
  808. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "men" );
  809. }
  810. else {
  811. $return = "Unsupported remoteControl command: " . @$a[2];
  812. }
  813. }
  814. }
  815. # play
  816. elsif ( lc( @$a[1] ) eq "play" ) {
  817. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  818. if ( $state ne "on" ) {
  819. $return =
  820. "Device power is turned off, this function is unavailable at that stage.";
  821. }
  822. else {
  823. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "play" );
  824. }
  825. }
  826. # pause
  827. elsif ( lc( @$a[1] ) eq "pause" ) {
  828. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  829. if ( $state ne "on" ) {
  830. $return =
  831. "Device power is turned off, this function is unavailable at that stage.";
  832. }
  833. else {
  834. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "pause" );
  835. }
  836. }
  837. # stop
  838. elsif ( lc( @$a[1] ) eq "stop" ) {
  839. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  840. if ( $state ne "on" ) {
  841. $return =
  842. "Device power is turned off, this function is unavailable at that stage.";
  843. }
  844. else {
  845. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "stop" );
  846. }
  847. }
  848. # shuffle
  849. elsif ( lc( @$a[1] ) eq "shuffle" || lc( @$a[1] ) eq "shufflet" ) {
  850. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  851. if ( $state ne "on" ) {
  852. $return =
  853. "Device power is turned off, this function is unavailable at that stage.";
  854. }
  855. else {
  856. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" );
  857. }
  858. }
  859. # repeat
  860. elsif ( lc( @$a[1] ) eq "repeat" || lc( @$a[1] ) eq "repeatt" ) {
  861. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  862. if ( $state ne "on" ) {
  863. $return =
  864. "Device power is turned off, this function is unavailable at that stage.";
  865. }
  866. else {
  867. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "repeat" );
  868. }
  869. }
  870. # previous
  871. elsif ( lc( @$a[1] ) eq "previous" ) {
  872. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  873. if ( $state ne "on" ) {
  874. $return =
  875. "Device power is turned off, this function is unavailable at that stage.";
  876. }
  877. else {
  878. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" );
  879. }
  880. }
  881. # next
  882. elsif ( lc( @$a[1] ) eq "next" ) {
  883. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  884. if ( $state ne "on" ) {
  885. $return =
  886. "Device power is turned off, this function is unavailable at that stage.";
  887. }
  888. else {
  889. $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" );
  890. }
  891. }
  892. # sleep
  893. elsif ( lc( @$a[1] ) eq "sleep" ) {
  894. if ( !defined( @$a[2] ) ) {
  895. $return = "No argument given, choose one of minutes off";
  896. }
  897. else {
  898. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  899. if ( @$a[2] eq "off" ) {
  900. $return = ONKYO_AVR_SendCommand( $hash, "sleep", "off" );
  901. }
  902. elsif ( @$a[2] =~ m/^\d+$/ && @$a[2] > 0 && @$a[2] <= 90 ) {
  903. $return =
  904. ONKYO_AVR_SendCommand( $hash, "sleep",
  905. ONKYO_AVR_dec2hex( @$a[2] ) );
  906. }
  907. else {
  908. $return =
  909. "Argument does not seem to be a valid integer between 0 and 90";
  910. }
  911. }
  912. }
  913. # mute
  914. elsif ( lc( @$a[1] ) eq "mute" || lc( @$a[1] ) eq "mutet" ) {
  915. if ( defined( @$a[2] ) ) {
  916. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  917. }
  918. else {
  919. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  920. }
  921. if ( $state eq "on" ) {
  922. if ( !defined( @$a[2] ) || @$a[2] eq "toggle" ) {
  923. $return = ONKYO_AVR_SendCommand( $hash, "mute", "toggle" );
  924. }
  925. elsif ( lc( @$a[2] ) eq "off" ) {
  926. $return = ONKYO_AVR_SendCommand( $hash, "mute", "off" );
  927. }
  928. elsif ( lc( @$a[2] ) eq "on" ) {
  929. $return = ONKYO_AVR_SendCommand( $hash, "mute", "on" );
  930. }
  931. else {
  932. $return = "Argument does not seem to be one of on off toogle";
  933. }
  934. }
  935. else {
  936. $return = "Device needs to be ON to mute/unmute audio.";
  937. }
  938. }
  939. # volume
  940. elsif ( lc( @$a[1] ) eq "volume" ) {
  941. if ( !defined( @$a[2] ) ) {
  942. $return = "No argument given";
  943. }
  944. else {
  945. my $volm = AttrVal( $name, "volumeMax", 0 );
  946. @$a[2] = $volm if ( $volm && @$a[2] > $volm );
  947. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  948. if ( $state eq "on" ) {
  949. if ( @$a[2] =~ m/^\d+$/ && @$a[2] >= 0 && @$a[2] <= 100 ) {
  950. $return =
  951. ONKYO_AVR_SendCommand( $hash, "volume",
  952. ONKYO_AVR_dec2hex( @$a[2] ) );
  953. }
  954. else {
  955. $return =
  956. "Argument does not seem to be a valid integer between 0 and 100";
  957. }
  958. }
  959. else {
  960. $return = "Device needs to be ON to adjust volume.";
  961. }
  962. }
  963. }
  964. # volumeUp/volumeDown
  965. elsif ( lc( @$a[1] ) =~ /^(volumeup|volumedown)$/ ) {
  966. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  967. my $volumeSteps = AttrVal( $name, "volumeSteps", "1" );
  968. my $volume = ReadingsVal( $name, "volume", "0" );
  969. if ( $state eq "on" ) {
  970. if ( lc( @$a[1] ) eq "volumeup" ) {
  971. if ( $volumeSteps > 1 ) {
  972. $return =
  973. ONKYO_AVR_SendCommand( $hash, "volume",
  974. ONKYO_AVR_dec2hex( $volume + $volumeSteps ) );
  975. }
  976. else {
  977. $return =
  978. ONKYO_AVR_SendCommand( $hash, "volume", "level-up" );
  979. }
  980. }
  981. else {
  982. if ( $volumeSteps > 1 ) {
  983. $return =
  984. ONKYO_AVR_SendCommand( $hash, "volume",
  985. ONKYO_AVR_dec2hex( $volume - $volumeSteps ) );
  986. }
  987. else {
  988. $return =
  989. ONKYO_AVR_SendCommand( $hash, "volume", "level-down" );
  990. }
  991. }
  992. }
  993. else {
  994. $return = "Device needs to be ON to adjust volume.";
  995. }
  996. }
  997. # input
  998. elsif ( lc( @$a[1] ) eq "input" ) {
  999. if ( !defined( @$a[2] ) ) {
  1000. $return = "No input given";
  1001. }
  1002. else {
  1003. if ( $state eq "off" ) {
  1004. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  1005. $return .= fhem "sleep 2;set $name input " . @$a[2];
  1006. }
  1007. else {
  1008. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  1009. $return = ONKYO_AVR_SendCommand( $hash, "input", @$a[2] );
  1010. }
  1011. }
  1012. }
  1013. # inputUp
  1014. elsif ( lc( @$a[1] ) eq "inputup" ) {
  1015. if ( $state eq "off" ) {
  1016. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  1017. $return .= fhem "sleep 2;set $name inputUp";
  1018. }
  1019. else {
  1020. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  1021. $return = ONKYO_AVR_SendCommand( $hash, "input", "up" );
  1022. }
  1023. }
  1024. # inputDown
  1025. elsif ( lc( @$a[1] ) eq "inputdown" ) {
  1026. if ( $state eq "off" ) {
  1027. $return = ONKYO_AVR_SendCommand( $hash, "power", "on" );
  1028. $return .= fhem "sleep 2;set $name inputDown";
  1029. }
  1030. else {
  1031. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1];
  1032. $return = ONKYO_AVR_SendCommand( $hash, "input", "down" );
  1033. }
  1034. }
  1035. # implicit commands through available readings
  1036. elsif ( grep $_ eq @$a[1], @implicit_cmds ) {
  1037. Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2];
  1038. if ( !defined( @$a[2] ) ) {
  1039. $return = "No argument given";
  1040. }
  1041. else {
  1042. $return = ONKYO_AVR_SendCommand( $hash, @$a[1], @$a[2] );
  1043. }
  1044. }
  1045. # return usage hint
  1046. else {
  1047. $return = $usage;
  1048. }
  1049. readingsEndUpdate( $hash, 1 );
  1050. # return result
  1051. return $return;
  1052. }
  1053. sub ONKYO_AVR_Get($$$) {
  1054. my ( $hash, $a, $h ) = @_;
  1055. my $name = $hash->{NAME};
  1056. my $zone = $hash->{ZONE};
  1057. my $state = ReadingsVal( $name, "power", "off" );
  1058. my $presence = ReadingsVal( $name, "presence", "absent" );
  1059. my $commands = ONKYOdb::ONKYO_GetRemotecontrolCommand($zone);
  1060. my $commands_details = ONKYOdb::ONKYO_GetRemotecontrolCommandDetails($zone);
  1061. my $return;
  1062. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Get()";
  1063. return "Argument is missing" if ( int(@$a) < 1 );
  1064. # readings
  1065. return $hash->{READINGS}{ @$a[1] }{VAL}
  1066. if ( defined( $hash->{READINGS}{ @$a[1] } ) );
  1067. return "Device is offline and cannot be controlled at that stage."
  1068. if ( $presence eq "absent" );
  1069. # createZone
  1070. if ( lc( @$a[1] ) eq "createzone" ) {
  1071. if ( !defined( @$a[2] ) ) {
  1072. $return = "Syntax: ZONE ID or NAME";
  1073. }
  1074. else {
  1075. $return =
  1076. fhem "define "
  1077. . $name . "_"
  1078. . @$a[2]
  1079. . " ONKYO_AVR_ZONE "
  1080. . @$a[2];
  1081. $return = $name . "_" . @$a[2] . " created"
  1082. if ( !$return || $return eq "" );
  1083. }
  1084. }
  1085. # statusRequest
  1086. elsif ( lc( @$a[1] ) eq "statusrequest" ) {
  1087. Log3 $name, 3, "ONKYO_AVR get $name " . @$a[1];
  1088. ONKYO_AVR_SendCommand( $hash, "power", "query" );
  1089. ONKYO_AVR_SendCommand( $hash, "input", "query" );
  1090. ONKYO_AVR_SendCommand( $hash, "mute", "query" );
  1091. ONKYO_AVR_SendCommand( $hash, "volume", "query" );
  1092. ONKYO_AVR_SendCommand( $hash, "sleep", "query" );
  1093. ONKYO_AVR_SendCommand( $hash, "audio-information", "query" );
  1094. ONKYO_AVR_SendCommand( $hash, "video-information", "query" );
  1095. ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" );
  1096. ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" );
  1097. ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" );
  1098. ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" );
  1099. ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level", "query" );
  1100. fhem
  1101. "sleep 1 quiet;get $name remoteControl net-receiver-information query quiet";
  1102. }
  1103. # remoteControl
  1104. elsif ( lc( @$a[1] ) eq "remotecontrol" ) {
  1105. # Output help for commands
  1106. if ( !defined( @$a[2] ) || @$a[2] eq "help" || @$a[2] eq "?" ) {
  1107. my $valid_commands =
  1108. "Usage: <command> <value>\n\nValid commands in zone$zone:\n\n\n"
  1109. . "COMMAND\t\t\tDESCRIPTION\n\n";
  1110. # For each valid command
  1111. foreach my $command ( sort keys %{$commands} ) {
  1112. my $command_raw = $commands->{$command};
  1113. # add command including description if found
  1114. if ( defined( $commands_details->{$command_raw}{description} ) )
  1115. {
  1116. $valid_commands .=
  1117. $command
  1118. . "\t\t\t"
  1119. . $commands_details->{$command_raw}{description} . "\n";
  1120. }
  1121. # add command only
  1122. else {
  1123. $valid_commands .= $command . "\n";
  1124. }
  1125. }
  1126. $valid_commands .=
  1127. "\nTry '&lt;command&gt; help' to find out well known values.\n\n\n";
  1128. $return = $valid_commands;
  1129. }
  1130. else {
  1131. # Reading values for command from HASH table
  1132. my $values =
  1133. ONKYOdb::ONKYO_GetRemotecontrolValue( $zone,
  1134. $commands->{ @$a[2] } );
  1135. @$a[3] = "query"
  1136. if ( !defined( @$a[3] ) && defined( $values->{query} ) );
  1137. # Output help for values
  1138. if ( !defined( @$a[3] ) || @$a[3] eq "help" || @$a[3] eq "?" ) {
  1139. # Get all details for command
  1140. my $command_details =
  1141. ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone,
  1142. $commands->{ @$a[2] } );
  1143. my $valid_values =
  1144. "Usage: "
  1145. . @$a[2]
  1146. . " <value>\n\nWell known values:\n\n\n"
  1147. . "VALUE\t\t\tDESCRIPTION\n\n";
  1148. # For each valid value
  1149. foreach my $value ( sort keys %{$values} ) {
  1150. # add value including description if found
  1151. if ( defined( $command_details->{description} ) ) {
  1152. $valid_values .=
  1153. $value
  1154. . "\t\t\t"
  1155. . $command_details->{description} . "\n";
  1156. }
  1157. # add value only
  1158. else {
  1159. $valid_values .= $value . "\n";
  1160. }
  1161. }
  1162. $valid_values .= "\n\n\n";
  1163. $return = $valid_values;
  1164. }
  1165. # normal processing
  1166. else {
  1167. Log3 $name, 3,
  1168. "ONKYO_AVR get $name " . @$a[1] . " " . @$a[2] . " " . @$a[3]
  1169. if ( !@$a[4] || @$a[4] ne "quiet" );
  1170. ONKYO_AVR_SendCommand( $hash, @$a[2], @$a[3] );
  1171. $return = "Sent command: " . @$a[2] . " " . @$a[3]
  1172. if ( !@$a[4] || @$a[4] ne "quiet" );
  1173. }
  1174. }
  1175. }
  1176. else {
  1177. $return =
  1178. "Unknown argument " . @$a[1] . ", choose one of statusRequest:noArg";
  1179. # createZone
  1180. my $zones = "";
  1181. if ( defined( $hash->{helper}{receiver}{device}{zonelist}{zone} ) ) {
  1182. foreach my $zoneID (
  1183. keys %{ $hash->{helper}{receiver}{device}{zonelist}{zone} } )
  1184. {
  1185. next
  1186. if (
  1187. !defined(
  1188. $hash->{helper}{receiver}{device}{zonelist}{zone}
  1189. {$zoneID}{value}
  1190. )
  1191. || $hash->{helper}{receiver}{device}{zonelist}{zone}
  1192. {$zoneID}{value} ne "1"
  1193. || $zoneID eq "1"
  1194. );
  1195. $zones .= "," if ( $zones ne "" );
  1196. $zones .= $zoneID;
  1197. }
  1198. }
  1199. $return .= " createZone:$zones" if ( $zones ne "" );
  1200. $return .= " createZone:2,3,4" if ( $zones eq "" );
  1201. # remoteControl
  1202. $return .= " remoteControl:";
  1203. foreach my $command ( sort keys %{$commands} ) {
  1204. $return .= "," . $command;
  1205. }
  1206. }
  1207. return $return;
  1208. }
  1209. sub ONKYO_AVR_Read($) {
  1210. my ($hash) = @_;
  1211. my $name = $hash->{NAME};
  1212. my $state = ReadingsVal( $name, "power", "off" );
  1213. my $zone = 0;
  1214. my $definedZones = scalar keys %{ $modules{ONKYO_AVR_ZONE}{defptr}{$name} };
  1215. # read from serial device
  1216. my $buf = DevIo_SimpleRead($hash);
  1217. return "" if ( !defined($buf) );
  1218. $buf = $hash->{PARTIAL} . $buf;
  1219. # reset connectionCheck timer
  1220. my $checkInterval = AttrVal( $name, "connectionCheck", "60" );
  1221. RemoveInternalTimer($hash);
  1222. if ( $checkInterval ne "off" ) {
  1223. my $next = gettimeofday() + $checkInterval;
  1224. $hash->{helper}{nextConnectionCheck} = $next;
  1225. InternalTimer( $next, "ONKYO_AVR_connectionCheck", $hash, 0 );
  1226. }
  1227. Log3 $name, 5, "ONKYO_AVR $name: raw " . ONKYO_AVR_hexdump($buf);
  1228. my $lastchr = substr( $buf, -1, 1 );
  1229. if ( $lastchr ne "\n" ) {
  1230. $hash->{PARTIAL} = $buf;
  1231. Log3( $hash, 5, "ONKYO_AVR_Read: partial command received" );
  1232. return;
  1233. }
  1234. else {
  1235. $hash->{PARTIAL} = "";
  1236. }
  1237. my $length = length $buf;
  1238. return unless ( $length >= 16 );
  1239. my ( $magic, $header_size, $data_size, $version, $res1, $res2, $res3 ) =
  1240. unpack 'a4 N N C4', $buf;
  1241. Log3 $name, 5,
  1242. "ONKYO_AVR $name: Unexpected magic: expected 'ISCP', got '$magic'"
  1243. and return
  1244. unless ( $magic eq 'ISCP' );
  1245. Log3 $name, 5,
  1246. "ONKYO_AVR $name: unusual packet length: $length < $header_size + $data_size"
  1247. and return
  1248. unless ( $length >= $header_size + $data_size );
  1249. Log3 $name, 5,
  1250. "ONKYO_AVR $name: Unexpected version: expected '0x01', got '0x%02x' "
  1251. . $version
  1252. and return
  1253. unless ( $version == 0x01 );
  1254. Log3 $name, 5,
  1255. "ONKYO_AVR $name: Unexpected header size: expected '0x10', got '0x%02x' "
  1256. . $header_size
  1257. and return
  1258. unless ( $header_size == 0x10 );
  1259. substr $buf, 0, $header_size, '';
  1260. my $value_raw = substr $buf, 0, $data_size, '';
  1261. my $sd = substr $value_raw, 0, 2, '';
  1262. $value_raw =~ s/([\032\r\n]|[\032\r]|[\r\n]|[\r])+$//;
  1263. Log3 $name, 5,
  1264. "ONKYO_AVR $name: Unexpected start/destination: expected '!1', got '$sd'"
  1265. and return
  1266. unless ( $sd eq '!1' );
  1267. my $cmd_raw;
  1268. my $cmd;
  1269. my $value = "";
  1270. # conversion based on zone
  1271. foreach my $zoneID ( keys %{ $modules{ONKYO_AVR_ZONE}{defptr}{$name} } ) {
  1272. next
  1273. if (
  1274. defined(
  1275. $hash->{helper}{receiver}{device}{zonelist}{zone}{$zoneID}
  1276. {value}
  1277. )
  1278. && $hash->{helper}{receiver}{device}{zonelist}{zone}{$zoneID}{value}
  1279. ne "1"
  1280. );
  1281. my $commandDB = ONKYOdb::ONKYO_GetRemotecontrolCommandDetails($zoneID);
  1282. foreach my $key ( keys %{$commandDB} ) {
  1283. if ( $value_raw =~ s/^$key(.*)// ) {
  1284. $cmd_raw = $key;
  1285. $cmd = $commandDB->{$cmd_raw}{name};
  1286. $value_raw = $1;
  1287. # Decode input through device information
  1288. if ( $cmd eq "input"
  1289. && defined( $hash->{helper}{receiver} )
  1290. && ref( $hash->{helper}{receiver} ) eq "HASH"
  1291. && defined( $hash->{helper}{receiver}{input}{$value_raw} ) )
  1292. {
  1293. $value = $hash->{helper}{receiver}{input}{$value_raw};
  1294. Log3 $name, 5,
  1295. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): return zone$zoneID value '$value_raw' converted through device information to '"
  1296. . $value . "'";
  1297. }
  1298. # Decode through HASH table
  1299. elsif (
  1300. defined(
  1301. $commandDB->{$cmd_raw}{values}{"$value_raw"}{name}
  1302. )
  1303. )
  1304. {
  1305. if (
  1306. ref(
  1307. $commandDB->{$cmd_raw}{values}{"$value_raw"}{name}
  1308. ) eq "ARRAY"
  1309. )
  1310. {
  1311. $value =
  1312. $commandDB->{$cmd_raw}{values}{"$value_raw"}{name}[0];
  1313. Log3 $name, 5,
  1314. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): return zone$zoneID value '$value_raw' converted through ARRAY from HASH table to '"
  1315. . $value . "'";
  1316. }
  1317. else {
  1318. $value =
  1319. $commandDB->{$cmd_raw}{values}{"$value_raw"}{name};
  1320. Log3 $name, 5,
  1321. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): return zone$zoneID value '$value_raw' converted through VALUE from HASH table to '"
  1322. . $value . "'";
  1323. }
  1324. }
  1325. # return as decimal
  1326. elsif ($value_raw =~ m/^[0-9A-Fa-f][0-9A-Fa-f]$/
  1327. && $cmd_raw =~
  1328. /^(MVL|ZVL|VL3|VL4|SLP|PRS|PRZ|PR3|PR4|PRM|PTS|NPR|NPZ|NP3|NP4)$/
  1329. )
  1330. {
  1331. $value = ONKYO_AVR_hex2dec($value_raw);
  1332. Log3 $name, 5,
  1333. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): return zone$zoneID value '$value_raw' converted from HEX to DEC '$value'";
  1334. }
  1335. # just return the original return value if there is
  1336. # no decoding function
  1337. elsif ( lc($value_raw) ne "n/a" ) {
  1338. $value = $value_raw;
  1339. Log3 $name, 5,
  1340. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): unconverted return of zone$zoneID value '$value'";
  1341. }
  1342. # end here if we got N/A result (few exceptions)
  1343. elsif ($cmd ne "audio-information"
  1344. && $cmd ne "video-information" )
  1345. {
  1346. $value = $value_raw;
  1347. Log3 $name, 4,
  1348. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): device sent: zone$zoneID command unavailable";
  1349. return;
  1350. }
  1351. last;
  1352. }
  1353. }
  1354. if ($cmd_raw) {
  1355. $zone = $zoneID;
  1356. last;
  1357. }
  1358. }
  1359. if ( !$cmd_raw ) {
  1360. $cmd_raw = substr( $value_raw, 0, 3 );
  1361. $value_raw =~ s/^...//;
  1362. $cmd = "_" . $cmd_raw;
  1363. $value = $value_raw;
  1364. Log3 $name, 4,
  1365. "ONKYO_AVR $name: con $cmd($cmd_raw$value_raw): FAIL: Don't know how to convert, not in ONKYOdb or zone may not be defined: $cmd_raw$value_raw";
  1366. return if ( !$cmd_raw || $cmd_raw eq "" );
  1367. }
  1368. if ( $zone > 1 ) {
  1369. Log3 $hash, 5, "ONKYO_AVR $name dispatch: this is for zone$zone";
  1370. my $zoneDispatch;
  1371. $zoneDispatch->{INPUT_RAW} = $value_raw if ( $cmd eq "input" );
  1372. $zoneDispatch->{zone} = $zone;
  1373. $zoneDispatch->{$cmd} = $value;
  1374. Dispatch( $hash, $zoneDispatch, undef );
  1375. return;
  1376. }
  1377. # Parsing for zone1 (main)
  1378. #
  1379. Log3 $name, 4, "ONKYO_AVR $name: rcv $cmd = $value"
  1380. if ( $cmd ne "net-usb-jacket-art" && $cmd ne "net-usb-time-info" );
  1381. $hash->{INPUT} = $value_raw if ( $cmd eq "input" );
  1382. my $zoneDispatch;
  1383. # Update readings
  1384. readingsBeginUpdate($hash);
  1385. if ( $cmd eq "audio-information" ) {
  1386. my @audio_split = split( /,/, $value );
  1387. if ( scalar(@audio_split) >= 6 ) {
  1388. readingsBulkUpdate( $hash, "audin_src", $audio_split[0] )
  1389. if ( ReadingsVal( $name, "audin_src", "-" ) ne $audio_split[0] );
  1390. readingsBulkUpdate( $hash, "audin_enc", $audio_split[1] )
  1391. if ( ReadingsVal( $name, "audin_enc", "-" ) ne $audio_split[1] );
  1392. my ($audin_srate) = split( /[:\s]+/, $audio_split[2], 2 ) || "";
  1393. readingsBulkUpdate( $hash, "audin_srate", $audin_srate )
  1394. if ( ReadingsVal( $name, "audin_srate", "-" ) ne $audin_srate );
  1395. my ($audin_ch) = split( /[:\s]+/, $audio_split[3], 2 ) || "";
  1396. readingsBulkUpdate( $hash, "audin_ch", $audin_ch )
  1397. if ( ReadingsVal( $name, "audin_ch", "-" ) ne $audin_ch );
  1398. readingsBulkUpdate( $hash, "audout_mode", $audio_split[4] )
  1399. if (
  1400. ReadingsVal( $name, "audout_mode", "-" ) ne $audio_split[4] );
  1401. my ($audout_ch) = split( /[:\s]+/, $audio_split[5], 2 ) || "";
  1402. readingsBulkUpdate( $hash, "audout_ch", $audout_ch )
  1403. if ( ReadingsVal( $name, "audout_ch", "-" ) ne $audout_ch );
  1404. }
  1405. else {
  1406. foreach (
  1407. "audin_src", "audin_enc", "audin_srate",
  1408. "audin_ch", "audout_ch", "audout_mode",
  1409. )
  1410. {
  1411. readingsBulkUpdate( $hash, $_, "" )
  1412. if ( ReadingsVal( $name, $_, "-" ) ne "" );
  1413. }
  1414. }
  1415. }
  1416. elsif ( $cmd eq "video-information" ) {
  1417. my @video_split = split( /,/, $value );
  1418. if ( scalar(@video_split) >= 9 ) {
  1419. # Video-in resolution
  1420. my @vidin_res_string = split( / +/, $video_split[1] );
  1421. my $vidin_res;
  1422. if ( defined( $vidin_res_string[0] )
  1423. && defined( $vidin_res_string[2] )
  1424. && defined( $vidin_res_string[3] )
  1425. && uc( $vidin_res_string[0] ) ne "UNKNOWN"
  1426. && uc( $vidin_res_string[2] ) ne "UNKNOWN"
  1427. && uc( $vidin_res_string[3] ) ne "UNKNOWN" )
  1428. {
  1429. $vidin_res =
  1430. $vidin_res_string[0] . "x"
  1431. . $vidin_res_string[2]
  1432. . $vidin_res_string[3];
  1433. }
  1434. else {
  1435. $vidin_res = "";
  1436. }
  1437. # Video-out resolution
  1438. my @vidout_res_string = split( / +/, $video_split[5] );
  1439. my $vidout_res;
  1440. if ( defined( $vidout_res_string[0] )
  1441. && defined( $vidout_res_string[2] )
  1442. && defined( $vidout_res_string[3] )
  1443. && uc( $vidout_res_string[0] ) ne "UNKNOWN"
  1444. && uc( $vidout_res_string[2] ) ne "UNKNOWN"
  1445. && uc( $vidout_res_string[3] ) ne "UNKNOWN" )
  1446. {
  1447. $vidout_res =
  1448. $vidout_res_string[0] . "x"
  1449. . $vidout_res_string[2]
  1450. . $vidout_res_string[3];
  1451. }
  1452. else {
  1453. $vidout_res = "";
  1454. }
  1455. # Video-in color depth
  1456. my ($vidin_cdepth) =
  1457. split( /[:\s]+/, $video_split[3], 2 ) || "";
  1458. # Video-out color depth
  1459. my ($vidout_cdepth) =
  1460. split( /[:\s]+/, $video_split[7], 2 ) || "";
  1461. readingsBulkUpdate( $hash, "vidin_src", $video_split[0] )
  1462. if ( ReadingsVal( $name, "vidin_src", "-" ) ne $video_split[0] );
  1463. readingsBulkUpdate( $hash, "vidin_res", $vidin_res )
  1464. if ( ReadingsVal( $name, "vidin_res", "-" ) ne $vidin_res );
  1465. readingsBulkUpdate( $hash, "vidin_cspace", $video_split[2] )
  1466. if (
  1467. ReadingsVal( $name, "vidin_cspace", "-" ) ne $video_split[2] );
  1468. readingsBulkUpdate( $hash, "vidin_cdepth", $vidin_cdepth )
  1469. if ( ReadingsVal( $name, "vidin_cdepth", "-" ) ne $vidin_cdepth );
  1470. readingsBulkUpdate( $hash, "vidout_dst", $video_split[4] )
  1471. if ( ReadingsVal( $name, "vidout_dst", "-" ) ne $video_split[4] );
  1472. readingsBulkUpdate( $hash, "vidout_res", $vidout_res )
  1473. if ( ReadingsVal( $name, "vidout_res", "-" ) ne $vidout_res );
  1474. readingsBulkUpdate( $hash, "vidout_cspace", $video_split[6] )
  1475. if (
  1476. ReadingsVal( $name, "vidout_cspace", "-" ) ne $video_split[6] );
  1477. readingsBulkUpdate( $hash, "vidout_cdepth", $vidout_cdepth )
  1478. if (
  1479. ReadingsVal( $name, "vidout_cdepth", "-" ) ne $vidout_cdepth );
  1480. readingsBulkUpdate( $hash, "vidout_mode", $video_split[8] )
  1481. if (
  1482. ReadingsVal( $name, "vidout_mode", "-" ) ne $video_split[8] );
  1483. }
  1484. else {
  1485. foreach (
  1486. "vidin_src", "vidin_res", "vidin_cspace",
  1487. "vidin_cdepth", "vidout_dst", "vidout_res",
  1488. "vidout_cspace", "vidout_cdepth", "vidout_mode",
  1489. )
  1490. {
  1491. readingsBulkUpdate( $hash, $_, "" )
  1492. if ( ReadingsVal( $name, $_, "-" ) ne "" );
  1493. }
  1494. }
  1495. }
  1496. elsif ( $cmd eq "net-receiver-information" ) {
  1497. if ( $value =~ /^<\?xml/ ) {
  1498. no strict;
  1499. my $xml_parser = XML::Simple->new(
  1500. NormaliseSpace => 0,
  1501. KeepRoot => 0,
  1502. ForceArray => [ "zone", "netservice", "preset", "control" ],
  1503. SuppressEmpty => 0,
  1504. KeyAttr => {
  1505. zone => "id",
  1506. netservice => "id",
  1507. preset => "id",
  1508. control => "id",
  1509. },
  1510. );
  1511. delete $hash->{helper}{receiver};
  1512. eval { $hash->{helper}{receiver} = $xml_parser->XMLin($value); };
  1513. use strict;
  1514. # Safe input names
  1515. my $inputs;
  1516. foreach my $input (
  1517. @{ $hash->{helper}{receiver}{device}{selectorlist}{selector} } )
  1518. {
  1519. if ( $input->{value} eq "1"
  1520. && $input->{zone} ne "00"
  1521. && $input->{id} ne "80" )
  1522. {
  1523. my $id = uc( $input->{id} );
  1524. my $name = trim( $input->{name} );
  1525. $name =~ s/\s/_/g;
  1526. $hash->{helper}{receiver}{input}{$id} = $name;
  1527. $inputs .= $name . ":";
  1528. }
  1529. }
  1530. if ( !defined( $attr{$name}{inputs} ) ) {
  1531. $inputs = substr( $inputs, 0, -1 );
  1532. $attr{$name}{inputs} = $inputs;
  1533. }
  1534. # Safe preset names
  1535. my $presets;
  1536. foreach my $id (
  1537. keys %{ $hash->{helper}{receiver}{device}{presetlist}{preset} }
  1538. )
  1539. {
  1540. my $name = trim(
  1541. $hash->{helper}{receiver}{device}{presetlist}{preset}{$id}
  1542. {name} );
  1543. next if ( !$name || $name eq "" );
  1544. $name =~ s/\s/_/g;
  1545. $hash->{helper}{receiver}{preset}{$id} = $name;
  1546. }
  1547. # Zones
  1548. my $reading = "zones";
  1549. if ( defined( $hash->{helper}{receiver}{device}{zonelist}{zone} ) )
  1550. {
  1551. my $zones = "0";
  1552. foreach my $zoneID (
  1553. keys %{ $hash->{helper}{receiver}{device}{zonelist}{zone} }
  1554. )
  1555. {
  1556. next
  1557. if ( $hash->{helper}{receiver}{device}{zonelist}{zone}
  1558. {$zoneID}{value} ne "1" );
  1559. $zones++;
  1560. }
  1561. readingsBulkUpdate( $hash, $reading, $zones )
  1562. if ( ReadingsVal( $name, $reading, "" ) ne $zones );
  1563. }
  1564. # Brand
  1565. $reading = "brand";
  1566. if (
  1567. defined( $hash->{helper}{receiver}{device}{$reading} )
  1568. && ( !defined( $hash->{READINGS}{$reading}{VAL} )
  1569. || $hash->{READINGS}{$reading}{VAL} ne
  1570. $hash->{helper}{receiver}{device}{$reading} )
  1571. )
  1572. {
  1573. readingsBulkUpdate( $hash, $reading,
  1574. $hash->{helper}{receiver}{device}{$reading} );
  1575. }
  1576. # Model
  1577. $reading = "model";
  1578. if (
  1579. defined( $hash->{helper}{receiver}{device}{$reading} )
  1580. && ( !defined( $hash->{READINGS}{$reading}{VAL} )
  1581. || $hash->{READINGS}{$reading}{VAL} ne
  1582. $hash->{helper}{receiver}{device}{$reading} )
  1583. )
  1584. {
  1585. if ( !exists( $attr{$name}{model} )
  1586. || $attr{$name}{model} ne
  1587. $hash->{helper}{receiver}{device}{$reading} )
  1588. {
  1589. $attr{$name}{model} =
  1590. $hash->{helper}{receiver}{device}{$reading};
  1591. }
  1592. }
  1593. # Firmware version
  1594. $reading = "firmwareversion";
  1595. if (
  1596. defined( $hash->{helper}{receiver}{device}{$reading} )
  1597. && ( !defined( $hash->{READINGS}{$reading}{VAL} )
  1598. || $hash->{READINGS}{$reading}{VAL} ne
  1599. $hash->{helper}{receiver}{device}{$reading} )
  1600. )
  1601. {
  1602. readingsBulkUpdate( $hash, $reading,
  1603. $hash->{helper}{receiver}{device}{$reading} );
  1604. }
  1605. # device_id
  1606. $reading = "deviceid";
  1607. if (
  1608. defined( $hash->{helper}{receiver}{device}{id} )
  1609. && ( !defined( $hash->{READINGS}{$reading}{VAL} )
  1610. || $hash->{READINGS}{$reading}{VAL} ne
  1611. $hash->{helper}{receiver}{device}{id} )
  1612. )
  1613. {
  1614. readingsBulkUpdate( $hash, $reading,
  1615. $hash->{helper}{receiver}{device}{id} );
  1616. }
  1617. # device_year
  1618. $reading = "deviceyear";
  1619. if (
  1620. defined( $hash->{helper}{receiver}{device}{year} )
  1621. && ( !defined( $hash->{READINGS}{$reading}{VAL} )
  1622. || $hash->{READINGS}{$reading}{VAL} ne
  1623. $hash->{helper}{receiver}{device}{year} )
  1624. )
  1625. {
  1626. readingsBulkUpdate( $hash, $reading,
  1627. $hash->{helper}{receiver}{device}{year} );
  1628. }
  1629. }
  1630. # Input alias handling
  1631. #
  1632. if ( defined( $attr{$name}{inputs} ) ) {
  1633. my @inputs = split( ':', $attr{$name}{inputs} );
  1634. if (@inputs) {
  1635. foreach (@inputs) {
  1636. if (m/[^,\s]+(,[^,\s]+)+/) {
  1637. my @input_names = split( ',', $_ );
  1638. $input_names[1] =~ s/\s/_/g;
  1639. $hash->{helper}{receiver}{input_aliases}
  1640. { $input_names[0] } = $input_names[1];
  1641. $hash->{helper}{receiver}{input_names}
  1642. { $input_names[1] } = $input_names[0];
  1643. }
  1644. }
  1645. }
  1646. }
  1647. ONKYO_AVR_SendCommand( $hash, "input", "query" );
  1648. }
  1649. elsif ( $cmd eq "net-usb-device-status" ) {
  1650. if ( $value =~ /^(.)(.)(.)$/ ) {
  1651. # network-connection
  1652. my $netConnStatus = "none";
  1653. $netConnStatus = "ethernet" if ( $1 eq "E" );
  1654. $netConnStatus = "wireless" if ( $1 eq "W" );
  1655. readingsBulkUpdate( $hash, "networkConnection", $netConnStatus )
  1656. if ( ReadingsVal( $name, "networkConnection", "-" ) ne
  1657. $netConnStatus );
  1658. # usbFront
  1659. my $usbFront = "none";
  1660. $usbFront = "iOS" if ( $2 eq "i" );
  1661. $usbFront = "Memory_NAS" if ( $2 eq "M" );
  1662. $usbFront = "wireless" if ( $2 eq "W" );
  1663. $usbFront = "bluetooth" if ( $2 eq "B" );
  1664. $usbFront = "GoogleUSB" if ( $2 eq "G" );
  1665. $usbFront = "disabled" if ( $2 eq "x" );
  1666. readingsBulkUpdate( $hash, "USB_Front", $usbFront )
  1667. if ( ReadingsVal( $name, "USB_Front", "-" ) ne $usbFront );
  1668. # usbRear
  1669. my $usbRear = "none";
  1670. $usbRear = "iOS" if ( $3 eq "i" );
  1671. $usbRear = "Memory_NAS" if ( $3 eq "M" );
  1672. $usbRear = "wireless" if ( $3 eq "W" );
  1673. $usbRear = "bluetooth" if ( $3 eq "B" );
  1674. $usbRear = "GoogleUSB" if ( $3 eq "G" );
  1675. $usbRear = "disabled" if ( $3 eq "x" );
  1676. readingsBulkUpdate( $hash, "USB_Rear", $usbRear )
  1677. if ( ReadingsVal( $name, "USB_Rear", "-" ) ne $usbRear );
  1678. }
  1679. }
  1680. elsif ($cmd eq "net-usb-jacket-art"
  1681. && $value ne "on"
  1682. && $value ne "off" )
  1683. {
  1684. if ( $value =~ /^([012])([012])(.*)$/ ) {
  1685. my $type = "bmp";
  1686. $type = "jpg" if ( $1 eq "1" );
  1687. $type = "link" if ( $1 eq "2" );
  1688. $hash->{helper}{cover}{$type}{parts} = "1" if ( "$2" eq "0" );
  1689. $hash->{helper}{cover}{$type}{parts}++ if ( "$2" ne "0" );
  1690. $hash->{helper}{cover}{$type}{data} = "" if ( "$2" eq "0" );
  1691. $hash->{helper}{cover}{$type}{data} .= "$3"
  1692. if ( "$2" eq "0" || $hash->{helper}{cover}{$type}{data} ne "" );
  1693. Log3 $name, 4, "ONKYO_AVR $name: rcv $cmd($type) in progress, part "
  1694. . $hash->{helper}{cover}{$type}{parts};
  1695. # complete album art received
  1696. if ( $2 eq "2"
  1697. && $type eq "link"
  1698. && $hash->{helper}{cover}{$type}{data} ne "" )
  1699. {
  1700. $hash->{helper}{currentCover} =
  1701. $hash->{helper}{cover}{$type}{data};
  1702. readingsBulkUpdate( $hash, "currentAlbumArtURI", "" );
  1703. readingsBulkUpdate( $hash, "currentAlbumArtURL",
  1704. $hash->{helper}{currentCover} );
  1705. $zoneDispatch->{currentAlbumArtURI} = "";
  1706. $zoneDispatch->{currentAlbumArtURL} =
  1707. $hash->{helper}{currentCover};
  1708. }
  1709. elsif ($2 eq "2"
  1710. && $type ne "link"
  1711. && $hash->{helper}{cover}{$type}{data} ne "" )
  1712. {
  1713. my $AlbumArtName = $name . "_CurrentAlbumArt." . $type;
  1714. my $AlbumArtURI = AttrVal( "global", "modpath", "." )
  1715. . "/www/images/default/ONKYO_AVR/$AlbumArtName";
  1716. my $AlbumArtURL = "?/ONKYO_AVR/cover/$AlbumArtName";
  1717. mkpath( AttrVal( "global", "modpath", "." )
  1718. . '/www/images/default/ONKYO_AVR/' );
  1719. ONKYO_AVR_WriteFile( $AlbumArtURI,
  1720. ONKYO_AVR_hex2image( $hash->{helper}{cover}{$type}{data} )
  1721. );
  1722. Log3 $name, 4,
  1723. "ONKYO_AVR $name: rcv $cmd($type) completed in "
  1724. . $hash->{helper}{cover}{$type}{parts}
  1725. . " parts. Saved to $AlbumArtURI";
  1726. delete $hash->{helper}{cover}{$type}{data};
  1727. $hash->{helper}{currentCover} = $AlbumArtURI;
  1728. readingsBulkUpdate( $hash, "currentAlbumArtURI", $AlbumArtURI );
  1729. readingsBulkUpdate( $hash, "currentAlbumArtURL", $AlbumArtURL );
  1730. $zoneDispatch->{currentAlbumArtURI} = $AlbumArtURI;
  1731. $zoneDispatch->{currentAlbumArtURL} = $AlbumArtURL;
  1732. }
  1733. }
  1734. else {
  1735. Log3 $name, 4,
  1736. "ONKYO_AVR $name: received cover art tile could not be decoded: "
  1737. . $value;
  1738. }
  1739. }
  1740. # currentTrackPosition
  1741. # currentTrackDuration
  1742. elsif ( $cmd eq "net-usb-time-info" ) {
  1743. my @times = split( /\//, $value );
  1744. if (
  1745. gettimeofday() - time_str2num(
  1746. ReadingsTimestamp(
  1747. $name, "currentTrackPosition", "1970-01-01 01:00:00"
  1748. )
  1749. ) >= 5
  1750. )
  1751. {
  1752. readingsBulkUpdate( $hash, "currentTrackPosition", $times[0] )
  1753. if ( ReadingsVal( $name, "currentTrackPosition", "-" ) ne
  1754. $times[0] );
  1755. $zoneDispatch->{currentTrackPosition} = $times[0];
  1756. }
  1757. if ( ReadingsVal( $name, "currentTrackDuration", "-" ) ne $times[1] ) {
  1758. readingsBulkUpdate( $hash, "currentTrackDuration", $times[1] );
  1759. $zoneDispatch->{currentTrackDuration} = $times[1];
  1760. }
  1761. }
  1762. # currentArtist
  1763. elsif ( $cmd eq "net-usb-artist-name-info" ) {
  1764. readingsBulkUpdate( $hash, "currentArtist", $value )
  1765. if ( ReadingsVal( $name, "currentArtist", "-" ) ne $value );
  1766. $zoneDispatch->{currentArtist} = $value;
  1767. }
  1768. # currentAlbum
  1769. elsif ( $cmd eq "net-usb-album-name-info" ) {
  1770. readingsBulkUpdate( $hash, "currentAlbum", $value )
  1771. if ( ReadingsVal( $name, "currentAlbum", "-" ) ne $value );
  1772. $zoneDispatch->{currentAlbum} = $value;
  1773. }
  1774. # currentTitle
  1775. elsif ( $cmd eq "net-usb-title-name" ) {
  1776. readingsBulkUpdate( $hash, "currentTitle", $value )
  1777. if ( ReadingsVal( $name, "currentTitle", "-" ) ne $value );
  1778. $zoneDispatch->{currentTitle} = $value;
  1779. }
  1780. elsif ( $cmd eq "net-usb-list-title-info" ) {
  1781. if ( $value =~ /^(..)(.)(.)(....)(....)(..)(..)(..)(..)(..)(.*)$/ ) {
  1782. # channel
  1783. my $channel = $1 || "00";
  1784. my $channelUc = uc($channel);
  1785. $hash->{CHANNEL} = $channel;
  1786. $channel = lc($channel);
  1787. my $channelname = "";
  1788. # Get all details for command
  1789. my $command_details =
  1790. ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( "1",
  1791. ONKYOdb::ONKYO_GetRemotecontrolCommand( "1", "net-service" ) );
  1792. # we know the channel name from receiver info
  1793. if (
  1794. defined( $hash->{helper}{receiver} )
  1795. && ref( $hash->{helper}{receiver} ) eq "HASH"
  1796. && defined(
  1797. $hash->{helper}{receiver}{device}{netservicelist}
  1798. {netservice}{$channel}{name}
  1799. )
  1800. )
  1801. {
  1802. $channelname =
  1803. $hash->{helper}{receiver}{device}{netservicelist}
  1804. {netservice}{$channel}{name};
  1805. $channelname =~ s/\s/_/g;
  1806. }
  1807. # we know the channel name from ONKYOdb
  1808. elsif ( defined( $command_details->{values}{$channelUc} ) ) {
  1809. if (
  1810. ref( $command_details->{values}{$channelUc}{name} ) eq
  1811. "ARRAY" )
  1812. {
  1813. $channelname =
  1814. $command_details->{values}{$channelUc}{name}[0];
  1815. }
  1816. else {
  1817. $channelname = $command_details->{values}{$channelUc}{name};
  1818. }
  1819. }
  1820. # some specials for net-usb-list-title-info
  1821. elsif ( $channel =~ /^f./ ) {
  1822. $channelname = "USB_Front" if $channel eq "f0";
  1823. $channelname = "USB_Rear" if $channel eq "f1";
  1824. $channelname = "Internet_Radio" if $channel eq "f2";
  1825. $channelname = "" if $channel eq "f3";
  1826. }
  1827. # we don't know the channel name, sorry
  1828. else {
  1829. Log3 $name, 4,
  1830. "ONKYO_AVR $name: net-usb-list-title-info: received unknown channel ID $channel";
  1831. $channelname = $channel;
  1832. }
  1833. if ( ReadingsVal( $name, "channel", "-" ) ne $channelname ) {
  1834. my $currentAlbumArtURI = AttrVal( "global", "modpath", "." )
  1835. . "/FHEM/lib/UPnP/sonos_empty.jpg";
  1836. my $currentAlbumArtURL = "?/ONKYO_AVR/cover/empty.jpg";
  1837. readingsBulkUpdate( $hash, "channel", $channelname );
  1838. readingsBulkUpdate( $hash, "currentAlbum", "" )
  1839. if ( ReadingsVal( $name, "currentAlbum", "-" ) ne "" );
  1840. readingsBulkUpdate( $hash, "currentAlbumArtURI",
  1841. $currentAlbumArtURI )
  1842. if ( ReadingsVal( $name, "currentAlbumArtURI", "-" ) ne
  1843. $currentAlbumArtURI );
  1844. readingsBulkUpdate( $hash, "currentAlbumArtURL",
  1845. $currentAlbumArtURL )
  1846. if ( ReadingsVal( $name, "currentAlbumArtURL", "-" ) ne
  1847. $currentAlbumArtURL );
  1848. readingsBulkUpdate( $hash, "currentArtist", "" )
  1849. if ( ReadingsVal( $name, "currentArtist", "-" ) ne "" );
  1850. readingsBulkUpdate( $hash, "currentTitle", "" )
  1851. if ( ReadingsVal( $name, "currentTitle", "-" ) ne "" );
  1852. readingsBulkUpdate( $hash, "currentTrackPosition", "--:--" )
  1853. if ( ReadingsVal( $name, "currentTrackPosition", "-" ) ne
  1854. "--:--" );
  1855. readingsBulkUpdate( $hash, "currentTrackDuration", "--:--" )
  1856. if ( ReadingsVal( $name, "currentTrackDuration", "-" ) ne
  1857. "--:--" );
  1858. $zoneDispatch->{CHANNEL_RAW} = $hash->{CHANNEL};
  1859. $zoneDispatch->{channel} = $channelname;
  1860. $zoneDispatch->{currentAlbum} = "";
  1861. $zoneDispatch->{currentAlbumArtURI} = $currentAlbumArtURI;
  1862. $zoneDispatch->{currentAlbumArtURL} = $currentAlbumArtURL;
  1863. $zoneDispatch->{currentArtist} = "";
  1864. $zoneDispatch->{currentTitle} = "";
  1865. $zoneDispatch->{currentTrackPosition} = "--:--";
  1866. $zoneDispatch->{currentTrackDuration} = "--:--";
  1867. }
  1868. # screenType
  1869. my $screenType = $2 || "0";
  1870. my $uiTypes = {
  1871. '0' => 'List',
  1872. '1' => 'Menu',
  1873. '2' => 'Playback',
  1874. '3' => 'Popup',
  1875. '4' => 'Keyboard',
  1876. '5' => 'Menu List',
  1877. };
  1878. my $uiType = $uiTypes->{$screenType};
  1879. readingsBulkUpdate( $hash, "screenType", $screenType )
  1880. if ( ReadingsVal( $name, "screenType", "-" ) ne $screenType );
  1881. # screenLayerInfo
  1882. my $screenLayerInfo = $3 || "0";
  1883. my $layerInfos = {
  1884. '0' => 'NET TOP',
  1885. '1' => 'Service Top,DLNA/USB/iPod',
  1886. '2' => 'under 2nd Layer',
  1887. };
  1888. my $layerInfo = $layerInfos->{$screenLayerInfo};
  1889. $hash->{SCREENLAYER} = $screenLayerInfo;
  1890. readingsBulkUpdate( $hash, "screenLayerInfo", $screenLayerInfo )
  1891. if ( readingsBulkUpdate( $hash, "screenLayerInfo", "" ) ne
  1892. $screenLayerInfo );
  1893. # screenListPos
  1894. my $screenListPos = $4 || "0000";
  1895. foreach my $line (
  1896. keys %{ $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list} } )
  1897. {
  1898. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$line}{listpos} =
  1899. 0;
  1900. }
  1901. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$screenListPos}
  1902. {listpos} = 1
  1903. if ( $screenListPos ne "-" );
  1904. readingsBulkUpdate( $hash, "screenListPos", $screenListPos )
  1905. if ( readingsBulkUpdate( $hash, "screenListPos", "" ) ne
  1906. $screenListPos );
  1907. # screenItemCnt
  1908. my $screenItemCnt = $5 || "0000";
  1909. readingsBulkUpdate( $hash, "screenItemCnt", $screenItemCnt )
  1910. if (
  1911. ReadingsVal( $name, "screenItemCnt", "-" ) ne $screenItemCnt );
  1912. # screenLayer
  1913. my $screenLayer = $6 || "00";
  1914. readingsBulkUpdate( $hash, "screenLayer", $screenLayer )
  1915. if ( ReadingsVal( $name, "screenLayer", "-" ) ne $screenLayer );
  1916. # my $reserved = $7;
  1917. my $screenIconLeft = $8 || "00";
  1918. my $iconsLeft = {
  1919. '00' => 'Internet Radio',
  1920. '01' => 'Server',
  1921. '02' => 'USB',
  1922. '03' => 'iPod',
  1923. '04' => 'DLNA',
  1924. '05' => 'WiFi',
  1925. '06' => 'Favorite',
  1926. '10' => 'Account(Spotify)',
  1927. '11' => 'Album(Spotify)',
  1928. '12' => 'Playlist(Spotify)',
  1929. '13' => 'Playlist-C(Spotify)',
  1930. '14' => 'Starred(Spotify)',
  1931. '15' => 'What\'s New(Spotify)',
  1932. '16' => 'Track(Spotify)',
  1933. '17' => 'Artist(Spotify)',
  1934. '18' => 'Play(Spotify)',
  1935. '19' => 'Search(Spotify)',
  1936. '1A' => 'Folder(Spotify)',
  1937. 'FF' => 'None'
  1938. };
  1939. my $iconLeft = $iconsLeft->{$screenIconLeft};
  1940. readingsBulkUpdate( $hash, "screenIconLeft", $screenIconLeft )
  1941. if ( ReadingsVal( $name, "screenIconLeft", "-" ) ne
  1942. $screenIconLeft );
  1943. my $screenIconRight = $9 || "00";
  1944. my $iconsRight = {
  1945. '00' => 'DLNA',
  1946. '01' => 'Favorite',
  1947. '02' => 'vTuner',
  1948. '03' => 'SiriusXM',
  1949. '04' => 'Pandora',
  1950. '05' => 'Rhapsody',
  1951. '06' => 'Last.fm',
  1952. '07' => 'Napster',
  1953. '08' => 'Slacker',
  1954. '09' => 'Mediafly',
  1955. '0A' => 'Spotify',
  1956. '0B' => 'AUPEO!',
  1957. '0C' => 'radiko',
  1958. '0D' => 'e-onkyo',
  1959. '0E' => 'TuneIn Radio',
  1960. '0F' => 'MP3tunes',
  1961. '10' => 'Simfy',
  1962. '11' => 'Home Media',
  1963. 'FF' => 'None'
  1964. };
  1965. my $iconRight = $iconsRight->{$screenIconRight};
  1966. readingsBulkUpdate( $hash, "screenIconRight", $screenIconRight )
  1967. if ( ReadingsVal( $name, "screenIconRight", "-" ) ne
  1968. $screenIconRight );
  1969. # screenStatus
  1970. my $screenStatus = $10 || "00";
  1971. my $statusInfos = {
  1972. '00' => '',
  1973. '01' => 'Connecting',
  1974. '02' => 'Acquiring License',
  1975. '03' => 'Buffering',
  1976. '04' => 'Cannot Play',
  1977. '05' => 'Searching',
  1978. '06' => 'Profile update',
  1979. '07' => 'Operation disabled',
  1980. '08' => 'Server Start-up',
  1981. '09' => 'Song rated as Favorite',
  1982. '0A' => 'Song banned from station',
  1983. '0B' => 'Authentication Failed',
  1984. '0C' => 'Spotify Paused(max 1 device)',
  1985. '0D' => 'Track Not Available',
  1986. '0E' => 'Cannot Skip'
  1987. };
  1988. my $statusInfo = $statusInfos->{$screenStatus};
  1989. if ( defined( $statusInfos->{$screenStatus} ) ) {
  1990. readingsBulkUpdate( $hash, "screenStatus",
  1991. $statusInfos->{$screenStatus} )
  1992. if ( ReadingsVal( $name, "screenStatus", "-" ) ne
  1993. $statusInfos->{$screenStatus} );
  1994. }
  1995. else {
  1996. readingsBulkUpdate( $hash, "screenStatus", $screenStatus )
  1997. if ( ReadingsVal( $name, "screenStatus", "-" ) ne
  1998. $screenStatus );
  1999. }
  2000. # screenTitle
  2001. my $screenTitle = $11 || "";
  2002. $screenTitle = "" if ( $screenTitle eq "NE" );
  2003. readingsBulkUpdate( $hash, "screenTitle", $screenTitle )
  2004. if ( ReadingsVal( $name, "screenTitle", "-" ) ne $screenTitle );
  2005. }
  2006. }
  2007. elsif ( $cmd eq "net-usb-menu-status" ) {
  2008. if ( $value =~ /^(.)(..)(..)(.)(.)(..)$/ ) {
  2009. my $menuState = $1;
  2010. }
  2011. }
  2012. # screen/list
  2013. elsif ( $cmd eq "net-usb-list-info" ) {
  2014. if ( $value =~ /^(.)(.)(.)(.*)/ ) {
  2015. my $item;
  2016. if ( $2 eq "-" ) {
  2017. $item = $2;
  2018. }
  2019. elsif ( $2 < 10 ) {
  2020. $item = "000" . $2;
  2021. }
  2022. elsif ( $2 < 100 ) {
  2023. $item = "00" . $2;
  2024. }
  2025. elsif ( $2 < 1000 ) {
  2026. $item = "0" . $2;
  2027. }
  2028. my $properties;
  2029. if ( $1 ne "C" ) {
  2030. $properties = {
  2031. '-' => 'no',
  2032. '0' => 'Playing',
  2033. 'A' => 'Artist',
  2034. 'B' => 'Album',
  2035. 'F' => 'Folder',
  2036. 'M' => 'Music',
  2037. 'P' => 'Playlist',
  2038. 'S' => 'Search',
  2039. 'a' => 'Account',
  2040. 'b' => 'Playlist-C',
  2041. 'c' => 'Starred',
  2042. 'd' => 'Unstarred',
  2043. 'e' => 'What\'s New'
  2044. };
  2045. }
  2046. # line item details
  2047. if ( $1 eq "A" || $1 eq "U" ) {
  2048. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$item}{property}
  2049. = $3;
  2050. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$item}{data} =
  2051. $4;
  2052. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$item}{curser} =
  2053. 0
  2054. if (
  2055. !defined(
  2056. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}
  2057. {$item}{curser}
  2058. )
  2059. );
  2060. # screenItemType
  2061. readingsBulkUpdate( $hash, "screenItemT" . $item, $3 )
  2062. if ( ReadingsVal( $name, "screenItemT" . $item, "-" ) ne $3 );
  2063. # screenItemContent
  2064. readingsBulkUpdate( $hash, "screenItemC" . $item, $4 )
  2065. if ( ReadingsVal( $name, "screenItemC" . $item, "-" ) ne $4 );
  2066. }
  2067. # curser information
  2068. else {
  2069. foreach my $item (
  2070. keys %{ $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list} } )
  2071. {
  2072. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$item}{curser}
  2073. = 0;
  2074. }
  2075. $hash->{SCREEN}{ $hash->{SCREENLAYER} }{list}{$item}{curser} = 1
  2076. if ( $item ne "-" );
  2077. readingsBulkUpdate( $hash, "screenCurser", $2 )
  2078. if ( ReadingsVal( $name, "screenCurser", "" ) ne $2 );
  2079. }
  2080. }
  2081. else {
  2082. Log3 $name, 4,
  2083. "ONKYO_AVR $name: screen/list: ERROR - unable to parse: "
  2084. . $value;
  2085. }
  2086. }
  2087. # screen/list XML
  2088. elsif ( $cmd eq "net-usb-list-info-xml" ) {
  2089. if ( $value =~ /^(.)(....)(.)(.)(..)(.*)/ ) {
  2090. Log3 $name, 4, "ONKYO_AVR $name: rcv $cmd($1) unknown type"
  2091. and return
  2092. if ( $1 ne "X" );
  2093. Log3 $name, 4, "ONKYO_AVR $name: rcv $cmd($1) in progress";
  2094. my $uiTypes = {
  2095. '0' => 'List',
  2096. '1' => 'Menu',
  2097. '2' => 'Playback',
  2098. '3' => 'Popup',
  2099. '4' => 'Keyboard',
  2100. '5' => 'Menu List',
  2101. };
  2102. my $uiType = $uiTypes->{$4};
  2103. $hash->{helper}{listinfo}{$3}{$2} = $6;
  2104. }
  2105. else {
  2106. Log3 $name, 4,
  2107. "ONKYO_AVR $name: net-usb-list-info-xml could not be parsed: "
  2108. . $value;
  2109. }
  2110. }
  2111. elsif ( $cmd eq "net-usb-play-status" ) {
  2112. if ( $value =~ /^(.)(.)(.)$/ ) {
  2113. my $status;
  2114. # playStatus
  2115. $status = "stopped";
  2116. $status = "playing"
  2117. if ( $1 eq "P" );
  2118. $status = "paused"
  2119. if ( $1 eq "p" );
  2120. $status = "fast-forward"
  2121. if ( $1 eq "F" );
  2122. $status = "fast-rewind"
  2123. if ( $1 eq "R" );
  2124. $status = "interrupted"
  2125. if ( $1 eq "E" );
  2126. readingsBulkUpdate( $hash, "playStatus", $status )
  2127. if ( ReadingsVal( $name, "playStatus", "-" ) ne $status );
  2128. $zoneDispatch->{playStatus} = $status;
  2129. # stateAV
  2130. my $stateAV = ONKYO_AVR_GetStateAV($hash);
  2131. readingsBulkUpdate( $hash, "stateAV", $stateAV )
  2132. if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
  2133. if ( $status eq "stopped" ) {
  2134. my $currentAlbumArtURI = AttrVal( "global", "modpath", "." )
  2135. . "/FHEM/lib/UPnP/sonos_empty.jpg";
  2136. my $currentAlbumArtURL = "?/ONKYO_AVR/cover/empty.jpg";
  2137. readingsBulkUpdate( $hash, "currentAlbum", "" )
  2138. if ( ReadingsVal( $name, "currentAlbum", "-" ) ne "" );
  2139. readingsBulkUpdate( $hash, "currentAlbumArtURI",
  2140. $currentAlbumArtURI )
  2141. if ( ReadingsVal( $name, "currentAlbumArtURI", "-" ) ne
  2142. $currentAlbumArtURI );
  2143. readingsBulkUpdate( $hash, "currentAlbumArtURL",
  2144. $currentAlbumArtURL )
  2145. if ( ReadingsVal( $name, "currentAlbumArtURL", "-" ) ne
  2146. $currentAlbumArtURL );
  2147. readingsBulkUpdate( $hash, "currentArtist", "" )
  2148. if ( ReadingsVal( $name, "currentArtist", "-" ) ne "" );
  2149. readingsBulkUpdate( $hash, "currentTitle", "" )
  2150. if ( ReadingsVal( $name, "currentTitle", "-" ) ne "" );
  2151. readingsBulkUpdate( $hash, "currentTrackPosition", "--:--" )
  2152. if ( ReadingsVal( $name, "currentTrackPosition", "-" ) ne
  2153. "--:--" );
  2154. readingsBulkUpdate( $hash, "currentTrackDuration", "--:--" )
  2155. if ( ReadingsVal( $name, "currentTrackDuration", "-" ) ne
  2156. "--:--" );
  2157. $zoneDispatch->{currentAlbum} = "";
  2158. $zoneDispatch->{currentAlbumArtURI} = $currentAlbumArtURI;
  2159. $zoneDispatch->{currentAlbumArtURL} = $currentAlbumArtURL;
  2160. $zoneDispatch->{currentArtist} = "";
  2161. $zoneDispatch->{currentTitle} = "";
  2162. $zoneDispatch->{currentTrackPosition} = "--:--";
  2163. $zoneDispatch->{currentTrackDuration} = "--:--";
  2164. }
  2165. # repeat
  2166. $status = "-";
  2167. $status = "off"
  2168. if ( $2 eq "-" );
  2169. $status = "all"
  2170. if ( $2 eq "R" );
  2171. $status = "all-folder"
  2172. if ( $2 eq "F" );
  2173. $status = "one"
  2174. if ( $2 eq "1" );
  2175. readingsBulkUpdate( $hash, "repeat", $status )
  2176. if ( ReadingsVal( $name, "repeat", "-" ) ne $status );
  2177. $zoneDispatch->{repeat} = $status;
  2178. # shuffle
  2179. $status = "-";
  2180. $status = "off"
  2181. if ( $2 eq "-" );
  2182. $status = "on"
  2183. if ( $3 eq "S" );
  2184. $status = "on-album"
  2185. if ( $3 eq "A" );
  2186. $status = "on-folder"
  2187. if ( $3 eq "F" );
  2188. readingsBulkUpdate( $hash, "shuffle", $status )
  2189. if ( ReadingsVal( $name, "shuffle", "-" ) ne $status );
  2190. $zoneDispatch->{shuffle} = $status;
  2191. }
  2192. }
  2193. elsif ( $cmd =~ /^net-usb/ && $value ne "on" && $value ne "off" ) {
  2194. }
  2195. elsif ( $cmd =~ /^net-keyboard/ ) {
  2196. }
  2197. # net-popup-*
  2198. elsif ( $cmd eq "net-popup-message" ) {
  2199. if ( $value =~
  2200. /^(B|T|L)(.*[a-z])([A-Z].*[a-z.!?])(0|1|2)([A-Z].*[a-z])$/ )
  2201. {
  2202. readingsBulkUpdate( $hash, "net-popup-type", "top" )
  2203. if ( $1 eq "T" );
  2204. readingsBulkUpdate( $hash, "net-popup-type", "bottom" )
  2205. if ( $1 eq "B" );
  2206. readingsBulkUpdate( $hash, "net-popup-type", "list" )
  2207. if ( $1 eq "L" );
  2208. readingsBulkUpdate( $hash, "net-popup-title", $2 );
  2209. readingsBulkUpdate( $hash, "net-popup-text", $3 );
  2210. readingsBulkUpdate( $hash, "net-popup-button-position", "hidden" )
  2211. if ( $4 eq "0" || $4 eq "" );
  2212. readingsBulkUpdate( $hash, "net-popup-button-position", $4 )
  2213. if ( $4 ne "0" && $4 ne "" );
  2214. readingsBulkUpdate( $hash, "net-popup-button1-text", $5 );
  2215. $zoneDispatch->{"net-popup-type"} =
  2216. ReadingsVal( $name, "net-popup-type", "" );
  2217. $zoneDispatch->{"net-popup-title"} = $2;
  2218. $zoneDispatch->{"net-popup-text"} = $3;
  2219. $zoneDispatch->{"net-popup-button-position"} =
  2220. ReadingsVal( $name, "net-popup-button-position", "" );
  2221. $zoneDispatch->{"net-popup-button1-text"} = $5;
  2222. }
  2223. else {
  2224. Log3 $name, 4,
  2225. "ONKYO_AVR $name: Could not decompile net-popup-message: $value";
  2226. }
  2227. }
  2228. # tone-*
  2229. elsif ( $cmd =~ /^tone-/ ) {
  2230. if ( $value =~ /^B(..)T(..)$/ ) {
  2231. my $bass = $1;
  2232. my $treble = $2;
  2233. my $bassName = $cmd . "-bass";
  2234. my $trebleName = $cmd . "-treble";
  2235. my $prefixBass = "";
  2236. my $prefixTreble = "";
  2237. # tone-*-bass
  2238. $prefixBass = "-" if ( $bass =~ /^\-.*/ );
  2239. $bass = substr( $bass, 1 ) if ( $bass =~ /^[\+|\-].*/ );
  2240. $bass = $prefixBass . ONKYO_AVR_hex2dec($bass);
  2241. readingsBulkUpdate( $hash, $bassName, $bass )
  2242. if ( ReadingsVal( $name, $bassName, "-" ) ne $bass );
  2243. # tone-*-treble
  2244. $prefixTreble = "-" if ( $treble =~ /^\-.*/ );
  2245. $treble = substr( $treble, 1 ) if ( $treble =~ /^[\+|\-].*/ );
  2246. $treble = $prefixTreble . ONKYO_AVR_hex2dec($treble);
  2247. readingsBulkUpdate( $hash, $trebleName, $treble )
  2248. if ( ReadingsVal( $name, $trebleName, "-" ) ne $treble );
  2249. }
  2250. # tone-subwoofer
  2251. elsif ( $value =~ /^B(..)$/ ) {
  2252. my $bass = $1;
  2253. my $prefix = "";
  2254. $prefix = "-" if ( $bass =~ /^\-.*/ );
  2255. $bass = substr( $bass, 1 ) if ( $bass =~ /^[\+|\-].*/ );
  2256. $bass = $prefix . ONKYO_AVR_hex2dec($bass);
  2257. readingsBulkUpdate( $hash, $cmd, $bass )
  2258. if ( ReadingsVal( $name, $cmd, "-" ) ne $bass );
  2259. }
  2260. }
  2261. else {
  2262. if ( $cmd eq "input" ) {
  2263. # Input alias handling
  2264. if ( defined( $hash->{helper}{receiver}{input_aliases}{$value} ) ) {
  2265. Log3 $name, 4,
  2266. "ONKYO_AVR $name: Input aliasing '$value' to '"
  2267. . $hash->{helper}{receiver}{input_aliases}{$value} . "'";
  2268. $value = $hash->{helper}{receiver}{input_aliases}{$value};
  2269. }
  2270. }
  2271. # subwoofer-temporary-level
  2272. # center-temporary-level
  2273. elsif ($cmd eq "subwoofer-temporary-level"
  2274. || $cmd eq "center-temporary-level" )
  2275. {
  2276. my $prefix = "";
  2277. $prefix = "-" if ( $value =~ /^\-.*/ );
  2278. $value = substr( $value, 1 ) if ( $value =~ /^[\+|\-].*/ );
  2279. $value = $prefix . ONKYO_AVR_hex2dec($value);
  2280. }
  2281. # preset
  2282. elsif ( $cmd eq "preset" ) {
  2283. if ( defined( $hash->{helper}{receiver}{preset} ) ) {
  2284. foreach
  2285. my $id ( sort keys %{ $hash->{helper}{receiver}{preset} } )
  2286. {
  2287. my $presetName =
  2288. $hash->{helper}{receiver}{preset}{$id};
  2289. next if ( !$presetName || $presetName eq "" );
  2290. $presetName =~ s/\s/_/g;
  2291. if ( $id eq $value ) {
  2292. $value = $presetName;
  2293. last;
  2294. }
  2295. }
  2296. }
  2297. $value = "" if ( $value eq "0" );
  2298. $zoneDispatch->{preset} = $value;
  2299. }
  2300. readingsBulkUpdate( $hash, $cmd, $value )
  2301. if ( ReadingsVal( $name, $cmd, "-" ) ne $value );
  2302. # stateAV
  2303. my $stateAV = ONKYO_AVR_GetStateAV($hash);
  2304. readingsBulkUpdate( $hash, "stateAV", $stateAV )
  2305. if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
  2306. }
  2307. readingsEndUpdate( $hash, 1 );
  2308. if ( $zoneDispatch && $definedZones > 1 ) {
  2309. Log3 $name, 5,
  2310. "ONKYO_AVR $name: Forwarding information from main zone1 to slave zones";
  2311. Dispatch( $hash, $zoneDispatch, undef );
  2312. }
  2313. return;
  2314. }
  2315. sub ONKYO_AVR_Write($$) {
  2316. my ( $hash, $cmd ) = @_;
  2317. my $name = $hash->{NAME};
  2318. my $str = ONKYO_AVR_Pack( $cmd, $hash->{PROTOCOLVERSION} );
  2319. Log3 $name, 1,
  2320. "ONKYO_AVR $name: $hash->{DeviceName} snd ERROR - could not transcode $cmd to HEX command"
  2321. and return
  2322. if ( !$str );
  2323. # Log3 $name, 5,
  2324. # "ONKYO_AVR $name: $hash->{DeviceName} snd " . ONKYO_AVR_hexdump($str);
  2325. Log3 $name, 5, "ONKYO_AVR $name: $hash->{DeviceName} snd $str";
  2326. DevIo_SimpleWrite( $hash, "$str", 0 );
  2327. # do connection check latest after TIMEOUT
  2328. my $next = gettimeofday() + $hash->{TIMEOUT};
  2329. if ( !defined( $hash->{helper}{nextConnectionCheck} )
  2330. || $hash->{helper}{nextConnectionCheck} > $next )
  2331. {
  2332. $hash->{helper}{nextConnectionCheck} = $next;
  2333. RemoveInternalTimer($hash);
  2334. InternalTimer( $next, "ONKYO_AVR_connectionCheck", $hash, 0 );
  2335. }
  2336. }
  2337. sub ONKYO_AVR_Ready($) {
  2338. my ($hash) = @_;
  2339. my $name = $hash->{NAME};
  2340. if ( ReadingsVal( $name, "state", "disconnected" ) eq "disconnected" ) {
  2341. DevIo_OpenDev(
  2342. $hash, 1, undef,
  2343. sub() {
  2344. my ( $hash, $err ) = @_;
  2345. Log3 $name, 4, "ONKYO_AVR $name: $err" if ($err);
  2346. }
  2347. );
  2348. return;
  2349. }
  2350. # This is relevant for windows/USB only
  2351. my $po = $hash->{USBDev};
  2352. my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags );
  2353. if ($po) {
  2354. ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
  2355. }
  2356. return ( $InBytes && $InBytes > 0 );
  2357. }
  2358. sub ONKYO_AVR_Notify($$) {
  2359. my ( $hash, $dev ) = @_;
  2360. my $name = $hash->{NAME};
  2361. my $devName = $dev->{NAME};
  2362. my $definedZones = scalar keys %{ $modules{ONKYO_AVR_ZONE}{defptr}{$name} };
  2363. my $presence = ReadingsVal( $name, "presence", "-" );
  2364. return
  2365. if ( !$dev->{CHANGED} ); # Some previous notify deleted the array.
  2366. # work on global events related to us
  2367. if ( $devName eq "global" ) {
  2368. foreach my $change ( @{ $dev->{CHANGED} } ) {
  2369. if ( $change !~ /^(\w+)\s(\w+)\s?(\w*)\s?(.*)$/
  2370. || $2 ne $name )
  2371. {
  2372. return;
  2373. }
  2374. # DEFINED
  2375. # MODIFIED
  2376. elsif ( $1 eq "DEFINED" || $1 eq "MODIFIED" ) {
  2377. Log3 $hash, 5,
  2378. "ONKYO_AVR "
  2379. . $name
  2380. . ": processing my global event $1: $3 -> $4";
  2381. if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) {
  2382. DoTrigger( $name, "CONNECTED" );
  2383. }
  2384. else {
  2385. DoTrigger( $name, "DISCONNECTED" );
  2386. }
  2387. }
  2388. # unknown event
  2389. else {
  2390. Log3 $hash, 5,
  2391. "ONKYO_AVR "
  2392. . $name
  2393. . ": WONT BE processing my global event $1: $3 -> $4";
  2394. }
  2395. }
  2396. return;
  2397. }
  2398. # do nothing for any other device
  2399. elsif ( $devName ne $name ) {
  2400. return;
  2401. }
  2402. readingsBeginUpdate($hash);
  2403. foreach my $change ( @{ $dev->{CHANGED} } ) {
  2404. # DISCONNECTED
  2405. if ( $change eq "DISCONNECTED" ) {
  2406. Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change";
  2407. # disable connectionCheck and wait
  2408. # until DevIo reopened the connection
  2409. RemoveInternalTimer($hash);
  2410. readingsBulkUpdate( $hash, "presence", "absent" )
  2411. if ( $presence ne "absent" );
  2412. readingsBulkUpdate( $hash, "power", "off" )
  2413. if ( ReadingsVal( $name, "power", "on" ) ne "off" );
  2414. # stateAV
  2415. my $stateAV = ONKYO_AVR_GetStateAV($hash);
  2416. readingsBulkUpdate( $hash, "stateAV", $stateAV )
  2417. if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
  2418. # send to slaves
  2419. if ( $definedZones > 1 ) {
  2420. Log3 $name, 5,
  2421. "ONKYO_AVR $name: Dispatching state change to slaves";
  2422. Dispatch(
  2423. $hash,
  2424. {
  2425. "presence" => "absent",
  2426. "power" => "off",
  2427. },
  2428. undef
  2429. );
  2430. }
  2431. }
  2432. # CONNECTED
  2433. elsif ( $change eq "CONNECTED" ) {
  2434. Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change";
  2435. readingsBulkUpdate( $hash, "presence", "present" )
  2436. if ( $presence ne "present" );
  2437. # stateAV
  2438. my $stateAV = ONKYO_AVR_GetStateAV($hash);
  2439. readingsBulkUpdate( $hash, "stateAV", $stateAV )
  2440. if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
  2441. ONKYO_AVR_SendCommand( $hash, "power", "query" );
  2442. ONKYO_AVR_SendCommand( $hash, "network-standby", "query" );
  2443. ONKYO_AVR_SendCommand( $hash, "input", "query" );
  2444. ONKYO_AVR_SendCommand( $hash, "mute", "query" );
  2445. ONKYO_AVR_SendCommand( $hash, "volume", "query" );
  2446. ONKYO_AVR_SendCommand( $hash, "sleep", "query" );
  2447. ONKYO_AVR_SendCommand( $hash, "audio-information", "query" );
  2448. ONKYO_AVR_SendCommand( $hash, "video-information", "query" );
  2449. ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" );
  2450. ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" );
  2451. ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" );
  2452. ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" );
  2453. ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level",
  2454. "query" );
  2455. fhem
  2456. "sleep 1 quiet;get $name remoteControl net-receiver-information query quiet";
  2457. # send to slaves
  2458. if ( $definedZones > 1 ) {
  2459. Log3 $name, 5,
  2460. "ONKYO_AVR $name: Dispatching state change to slaves";
  2461. Dispatch(
  2462. $hash,
  2463. {
  2464. "presence" => "present",
  2465. },
  2466. undef
  2467. );
  2468. }
  2469. }
  2470. }
  2471. readingsEndUpdate( $hash, 1 );
  2472. }
  2473. sub ONKYO_AVR_Shutdown($) {
  2474. my ($hash) = @_;
  2475. my $name = $hash->{NAME};
  2476. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Shutdown()";
  2477. DevIo_CloseDev($hash);
  2478. return undef;
  2479. }
  2480. # module Fn ####################################################################
  2481. sub ONKYO_AVR_DevInit($) {
  2482. my ($hash) = @_;
  2483. my $name = $hash->{NAME};
  2484. if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) {
  2485. DoTrigger( $name, "CONNECTED" );
  2486. }
  2487. else {
  2488. DoTrigger( $name, "DISCONNECTED" );
  2489. }
  2490. }
  2491. sub ONKYO_AVR_addExtension($$$) {
  2492. my ( $name, $func, $link ) = @_;
  2493. my $url = "?/$link";
  2494. return 0
  2495. if ( defined( $data{FWEXT}{$url} )
  2496. && $data{FWEXT}{$url}{deviceName} ne $name );
  2497. Log3 $name, 2,
  2498. "ONKYO_AVR $name: Registering ONKYO_AVR for webhook URI $url ...";
  2499. $data{FWEXT}{$url}{deviceName} = $name;
  2500. $data{FWEXT}{$url}{FUNC} = $func;
  2501. $data{FWEXT}{$url}{LINK} = $link;
  2502. return 1;
  2503. }
  2504. sub ONKYO_AVR_removeExtension($) {
  2505. my ($link) = @_;
  2506. my $url = "?/$link";
  2507. my $name = $data{FWEXT}{$url}{deviceName};
  2508. Log3 $name, 2,
  2509. "ONKYO_AVR $name: Unregistering ONKYO_AVR for webhook URI $url...";
  2510. delete $data{FWEXT}{$url};
  2511. }
  2512. sub ONKYO_AVR_CGI() {
  2513. my ($request) = @_;
  2514. # data received
  2515. if ( $request =~ m,^\?\/ONKYO_AVR\/cover\/(.+)\.(.+)$, ) {
  2516. Log3 undef, 5, "ONKYO_AVR: sending cover $1.$2";
  2517. if ( $1 eq "empty" && $2 eq "jpg" ) {
  2518. FW_serveSpecial( 'sonos_empty', 'jpg',
  2519. AttrVal( "global", "modpath", "." ) . '/FHEM/lib/UPnP', 1 );
  2520. }
  2521. else {
  2522. FW_serveSpecial(
  2523. $1,
  2524. $2,
  2525. AttrVal( "global", "modpath", "." )
  2526. . '/www/images/default/ONKYO_AVR',
  2527. 1
  2528. );
  2529. }
  2530. return ( undef, undef );
  2531. }
  2532. # no data received
  2533. else {
  2534. Log3 undef, 5, "ONKYO_AVR: received malformed request\n$request";
  2535. }
  2536. return ( "text/plain; charset=utf-8", "Call failure: " . $request );
  2537. }
  2538. sub ONKYO_AVR_SendCommand($$$) {
  2539. my ( $hash, $cmd, $value ) = @_;
  2540. my $name = $hash->{NAME};
  2541. my $zone = $hash->{ZONE};
  2542. Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_SendCommand()";
  2543. # Input alias handling
  2544. if ( $cmd eq "input" ) {
  2545. # Resolve input alias to correct name
  2546. if ( defined( $hash->{helper}{receiver}{input_names}{$value} ) ) {
  2547. $value = $hash->{helper}{receiver}{input_names}{$value};
  2548. }
  2549. # Resolve device specific input alias
  2550. $value =~ s/_/ /g;
  2551. if (
  2552. defined(
  2553. $hash->{helper}{receiver}{device}{selectorlist}{selector}
  2554. )
  2555. && ref( $hash->{helper}{receiver}{device}{selectorlist}{selector} )
  2556. eq "ARRAY"
  2557. )
  2558. {
  2559. foreach my $input (
  2560. @{ $hash->{helper}{receiver}{device}{selectorlist}{selector} } )
  2561. {
  2562. if ( $input->{value} eq "1"
  2563. && $input->{zone} ne "00"
  2564. && $input->{id} ne "80"
  2565. && $value eq trim( $input->{name} ) )
  2566. {
  2567. $value = uc( $input->{id} );
  2568. last;
  2569. }
  2570. }
  2571. }
  2572. }
  2573. # Resolve command and value to ISCP raw command
  2574. my $cmd_raw = ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, $cmd );
  2575. my $value_raw =
  2576. ONKYOdb::ONKYO_GetRemotecontrolValue( $zone, $cmd_raw, $value );
  2577. if ( !defined($cmd_raw) ) {
  2578. Log3 $name, 4,
  2579. "ONKYO_AVR $name: command '$cmd$value' is an unregistered command within zone$zone, be careful! Will be handled as raw command";
  2580. $cmd_raw = $cmd;
  2581. $value_raw = $value;
  2582. }
  2583. elsif ( !defined($value_raw) ) {
  2584. Log3 $name, 4,
  2585. "ONKYO_AVR $name: $cmd - Warning, value '$value' not found in HASH table, will be sent to receiver 'as is'";
  2586. $value_raw = $value;
  2587. }
  2588. Log3 $name, 4, "ONKYO_AVR $name: snd $cmd -> $value ($cmd_raw$value_raw)";
  2589. if ( $cmd_raw ne "" && $value_raw ne "" ) {
  2590. ONKYO_AVR_Write( $hash, $cmd_raw . $value_raw );
  2591. }
  2592. return;
  2593. }
  2594. sub ONKYO_AVR_connectionCheck ($) {
  2595. my ($hash) = @_;
  2596. my $name = $hash->{NAME};
  2597. my $verbose = AttrVal( $name, "verbose", "" );
  2598. RemoveInternalTimer($hash);
  2599. $hash->{STATE} = "opened"; # assume we have an open connection
  2600. $attr{$name}{verbose} = 0 if ( $verbose eq "" || $verbose < 4 );
  2601. my $connState =
  2602. DevIo_Expect( $hash,
  2603. ONKYO_AVR_Pack( "PWRQSTN", $hash->{PROTOCOLVERSION} ),
  2604. $hash->{TIMEOUT} );
  2605. # successful connection
  2606. if ( defined($connState) ) {
  2607. # reset connectionCheck timer
  2608. my $checkInterval = AttrVal( $name, "connectionCheck", "60" );
  2609. if ( $checkInterval ne "off" ) {
  2610. my $next = gettimeofday() + $checkInterval;
  2611. $hash->{helper}{nextConnectionCheck} = $next;
  2612. InternalTimer( $next, "ONKYO_AVR_connectionCheck", $hash, 0 );
  2613. }
  2614. }
  2615. $attr{$name}{verbose} = $verbose if ( $verbose ne "" );
  2616. delete $attr{$name}{verbose} if ( $verbose eq "" );
  2617. }
  2618. sub ONKYO_AVR_WriteFile($$) {
  2619. my ( $fileName, $data ) = @_;
  2620. open IMGFILE, '>' . $fileName;
  2621. binmode IMGFILE;
  2622. print IMGFILE $data;
  2623. close IMGFILE;
  2624. }
  2625. sub ONKYO_AVR_Pack($;$) {
  2626. my ( $d, $protocol ) = @_;
  2627. # ------------------
  2628. # < 2013 (produced by TX-NR515)
  2629. # ------------------
  2630. #
  2631. # EXAMPLE REQUEST FOR PWRQSTN
  2632. # 4953 4350 0000 0010 0000 000a 0100 0000 ISCP............
  2633. # 2131 5057 5251 5354 4e0d !1PWRQSTN.
  2634. #
  2635. # EXAMPLE REPLY FOR PWRQSTN
  2636. # 4953 4350 0000 0010 0000 000a 0100 0000 ISCP............
  2637. # 2131 5057 5230 311a 0d0a !1PWR01...
  2638. #
  2639. # ------------------
  2640. # 2013+ (produced by TX-NR626)
  2641. # ------------------
  2642. #
  2643. # EXAMPLE REQUEST FOR PWRQSTN
  2644. # 4953 4350 0000 0010 0000 000b 0100 0000 ISCP............
  2645. # 2131 5057 5251 5354 4e0d 0a !1PWRQSTN..
  2646. #
  2647. # EXAMPLE REPLY FOR PWRQSTN
  2648. # 4953 4350 0000 0010 0000 000a 0100 0000 ISCP............
  2649. # 2131 5057 5230 311a 0d0a !1PWR01...
  2650. #
  2651. # add start character and destination unit type 1=receiver
  2652. $d = '!1' . $d;
  2653. # If protocol is defined as pre-2013 use EOF code for older models
  2654. if ( defined($protocol) && $protocol eq "pre2013" ) {
  2655. # <CR> = 0x0d
  2656. $d .= "\r";
  2657. }
  2658. # otherwise use EOF code for newer models
  2659. else {
  2660. # <CR><LF> = 0x0d0a
  2661. $d .= "\r\n";
  2662. }
  2663. pack( "a* N N N a*", 'ISCP', 0x10, ( length $d ), 0x01000000, $d );
  2664. }
  2665. sub ONKYO_AVR_hexdump {
  2666. my $s = shift;
  2667. my $r = unpack 'H*', $s;
  2668. $s =~ s/[^ -~]/./g;
  2669. $r . ' ' . $s;
  2670. }
  2671. sub ONKYO_AVR_hex2dec($) {
  2672. my ($hex) = @_;
  2673. return unpack( 's', pack 's', hex($hex) );
  2674. }
  2675. sub ONKYO_AVR_hex2image($) {
  2676. my ($hex) = @_;
  2677. return pack( "H*", $hex );
  2678. }
  2679. sub ONKYO_AVR_dec2hex($) {
  2680. my ($dec) = @_;
  2681. my $hex = uc( sprintf( "%x", $dec ) );
  2682. return "0" . $hex if ( length($hex) eq 1 );
  2683. return $hex;
  2684. }
  2685. sub ONKYO_AVR_GetStateAV($) {
  2686. my ($hash) = @_;
  2687. my $name = $hash->{NAME};
  2688. if ( ReadingsVal( $name, "presence", "absent" ) eq "absent" ) {
  2689. return "absent";
  2690. }
  2691. elsif ( ReadingsVal( $name, "power", "off" ) eq "off" ) {
  2692. return "off";
  2693. }
  2694. elsif ( ReadingsVal( $name, "mute", "off" ) eq "on" ) {
  2695. return "muted";
  2696. }
  2697. elsif ( $hash->{INPUT} eq "2B"
  2698. && ReadingsVal( $name, "playStatus", "stopped" ) ne "stopped" )
  2699. {
  2700. return ReadingsVal( $name, "playStatus", "stopped" );
  2701. }
  2702. else {
  2703. return ReadingsVal( $name, "power", "off" );
  2704. }
  2705. }
  2706. sub ONKYO_AVR_RCmakenotify($$) {
  2707. my ( $name, $ndev ) = @_;
  2708. my $nname = "notify_$name";
  2709. fhem( "define $nname notify $name set $ndev remoteControl " . '$EVENT', 1 );
  2710. Log3 undef, 2, "[remotecontrol:ONKYO_AVR] Notify created: $nname";
  2711. return "Notify created by ONKYO_AVR: $nname";
  2712. }
  2713. sub ONKYO_AVR_RClayout_SVG() {
  2714. my @row;
  2715. $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,power toggle:rc_POWER.svg";
  2716. $row[1] =
  2717. "volume level-up:rc_VOLUP.svg,mute toggle:rc_MUTE.svg,preset up:rc_UP.svg";
  2718. $row[2] =
  2719. "volume level-down:rc_VOLDOWN.svg,sleep:time_timer.svg,preset down:rc_DOWN.svg";
  2720. $row[3] = ":rc_BLANK.svg,tuning up:rc_UP.svg,:rc_BLANK.svg";
  2721. $row[4] = "left:rc_LEFT.svg,enter:rc_OK.svg,right:rc_RIGHT.svg";
  2722. $row[5] =
  2723. "input usb:rc_USB.svg,tuning down:rc_DOWN.svg,input dlna:rc_MEDIAMENU.svg";
  2724. $row[6] = "input tv-cd:rc_TV.svg,input fm:rc_RADIO.svg,input pc:it_pc.svg";
  2725. $row[7] = "attr rc_iconpath icons/remotecontrol";
  2726. $row[8] = "attr rc_iconprefix black_btn_";
  2727. return @row;
  2728. }
  2729. sub ONKYO_AVR_RClayout() {
  2730. my @row;
  2731. $row[0] =
  2732. "hdmi-output 01:HDMI_main,hdmi-output 02:HDMI_sub,power toggle:POWEROFF";
  2733. $row[1] = "volume level-up:VOLUP,mute toggle:MUTE,preset up:UP";
  2734. $row[2] = "volume level-down:VOLDOWN,sleep:SLEEP,preset down:DOWN";
  2735. $row[3] = ":blank,tuning up:UP,:blank";
  2736. $row[4] = "left:LEFT,enter:OK,right:RIGHT";
  2737. $row[5] = "input usb:SOURCE,tuning down:DOWN,input dlna:DLNA";
  2738. $row[6] = "input tv-cd:TV,input fm:FMRADIO,input pc:PC";
  2739. $row[7] = "attr rc_iconpath icons/remotecontrol";
  2740. $row[8] = "attr rc_iconprefix black_btn_";
  2741. return @row;
  2742. }
  2743. 1;
  2744. =pod
  2745. =item device
  2746. =item summary control for ONKYO AV receivers via network or serial connection
  2747. =item summary_DE Steuerung von ONKYO AV Receiver per Netzwerk oder seriell
  2748. =begin html
  2749. <p>
  2750. <a name="ONKYO_AVR" id="ONKYO_AVR"></a>
  2751. </p>
  2752. <h3>
  2753. ONKYO_AVR
  2754. </h3>
  2755. <ul>
  2756. <a name="ONKYO_AVRdefine" id="ONKYO_AVRdefine"></a> <b>Define</b>
  2757. <ul>
  2758. <code>define &lt;name&gt; ONKYO_AVR &lt;ip-address-or-hostname[:PORT]&gt; [&lt;protocol-version&gt;]</code><br>
  2759. <code>define &lt;name&gt; ONKYO_AVR &lt;devicename[@baudrate]&gt; [&lt;protocol-version&gt;]</code><br>
  2760. <br>
  2761. This module controls ONKYO A/V receivers in real-time via network connection.<br>
  2762. Some newer Pioneer A/V models seem to run ONKYO's ISCP protocol as well and therefore should be fully supported by this module.<br>
  2763. Use <a href="#ONKYO_AVR_ZONE">ONKYO_AVR_ZONE</a> to control slave zones.<br>
  2764. <br>
  2765. Instead of IP address or hostname you may set a serial connection format for direct connectivity.<br>
  2766. <br>
  2767. <br>
  2768. Example:<br>
  2769. <ul>
  2770. <code>
  2771. define avr ONKYO_AVR 192.168.0.10<br>
  2772. <br>
  2773. # With explicit port<br>
  2774. define avr ONKYO_AVR 192.168.0.10:60128<br>
  2775. <br>
  2776. # With explicit protocol version 2013 and later<br>
  2777. define avr ONKYO_AVR 192.168.0.10 2013<br>
  2778. <br>
  2779. # With protocol version prior 2013<br>
  2780. define avr ONKYO_AVR 192.168.0.10 pre2013
  2781. <br>
  2782. # With protocol version prior 2013 and serial connection<br>
  2783. define avr ONKYO_AVR /dev/ttyUSB1@9600 pre2013
  2784. </code>
  2785. </ul>
  2786. </ul><br>
  2787. <br>
  2788. <a name="ONKYO_AVRset" id="ONKYO_AVRset"></a> <b>Set</b>
  2789. <ul>
  2790. <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code><br>
  2791. <br>
  2792. Currently, the following commands are defined:<br>
  2793. <ul>
  2794. <li>
  2795. <b>channel</b> &nbsp;&nbsp;-&nbsp;&nbsp; set active network service (e.g. Spotify)
  2796. </li>
  2797. <li>
  2798. <b>currentTrackPosition</b> &nbsp;&nbsp;-&nbsp;&nbsp; seek to specific time for current track
  2799. </li>
  2800. <li>
  2801. <b>input</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches between inputs
  2802. </li>
  2803. <li>
  2804. <b>inputDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches one input down
  2805. </li>
  2806. <li>
  2807. <b>inputUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches one input up
  2808. </li>
  2809. <li>
  2810. <b>mute</b> on,off &nbsp;&nbsp;-&nbsp;&nbsp; controls volume mute
  2811. </li>
  2812. <li>
  2813. <b>muteT</b> &nbsp;&nbsp;-&nbsp;&nbsp; toggle mute state
  2814. </li>
  2815. <li>
  2816. <b>next</b> &nbsp;&nbsp;-&nbsp;&nbsp; skip track
  2817. </li>
  2818. <li>
  2819. <b>off</b> &nbsp;&nbsp;-&nbsp;&nbsp; turns the device in standby mode
  2820. </li>
  2821. <li>
  2822. <b>on</b> &nbsp;&nbsp;-&nbsp;&nbsp; powers on the device
  2823. </li>
  2824. <li>
  2825. <b>pause</b> &nbsp;&nbsp;-&nbsp;&nbsp; pause current playback
  2826. </li>
  2827. <li>
  2828. <b>play</b> &nbsp;&nbsp;-&nbsp;&nbsp; start playback
  2829. </li>
  2830. <li>
  2831. <b>power</b> on,off &nbsp;&nbsp;-&nbsp;&nbsp; set power mode
  2832. </li>
  2833. <li>
  2834. <b>preset</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches between presets
  2835. </li>
  2836. <li>
  2837. <b>presetDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches one preset down
  2838. </li>
  2839. <li>
  2840. <b>presetUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches one preset up
  2841. </li>
  2842. <li>
  2843. <b>previous</b> &nbsp;&nbsp;-&nbsp;&nbsp; back to previous track
  2844. </li>
  2845. <li>
  2846. <b>remoteControl</b> Send specific remoteControl command to device
  2847. </li>
  2848. <li>
  2849. <b>repeat</b> off,all,all-folder,one &nbsp;&nbsp;-&nbsp;&nbsp; set repeat setting
  2850. </li>
  2851. <li>
  2852. <b>repeatT</b> &nbsp;&nbsp;-&nbsp;&nbsp; toggle repeat state
  2853. </li>
  2854. <li>
  2855. <b>shuffle</b> off,on,on-album,on-folder &nbsp;&nbsp;-&nbsp;&nbsp; set shuffle setting
  2856. </li>
  2857. <li>
  2858. <b>shuffleT</b> &nbsp;&nbsp;-&nbsp;&nbsp; toggle shuffle state
  2859. </li>
  2860. <li>
  2861. <b>sleep</b> 1..90,off &nbsp;&nbsp;-&nbsp;&nbsp; sets auto-turnoff after X minutes
  2862. </li>
  2863. <li>
  2864. <b>stop</b> &nbsp;&nbsp;-&nbsp;&nbsp; stop current playback
  2865. </li>
  2866. <li>
  2867. <b>toggle</b> &nbsp;&nbsp;-&nbsp;&nbsp; switch between on and off
  2868. </li>
  2869. <li>
  2870. <b>volume</b> 0...100 &nbsp;&nbsp;-&nbsp;&nbsp; set the volume level in percentage
  2871. </li>
  2872. <li>
  2873. <b>volumeUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; increases the volume level
  2874. </li>
  2875. <li>
  2876. <b>volumeDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; decreases the volume level
  2877. </li>
  2878. </ul>
  2879. <ul>
  2880. <br>
  2881. Other set commands may appear dynamically based on previously used "get avr remoteControl"-commands and resulting readings.<br>
  2882. See "get avr remoteControl &lt;Set-name&gt; help" to get more information about possible readings and set values.
  2883. </ul>
  2884. </ul><br>
  2885. <br>
  2886. <a name="ONKYO_AVRget" id="ONKYO_AVRget"></a> <b>Get</b>
  2887. <ul>
  2888. <code>get &lt;name&gt; &lt;what&gt;</code><br>
  2889. <br>
  2890. Currently, the following commands are defined:<br>
  2891. <br>
  2892. <ul>
  2893. <li>
  2894. <b>createZone</b> &nbsp;&nbsp;-&nbsp;&nbsp; creates a separate <a href="#ONKYO_AVR_ZONE">ONKYO_AVR_ZONE</a> device for available zones of the device
  2895. </li>
  2896. <li>
  2897. <b>remoteControl</b> &nbsp;&nbsp;-&nbsp;&nbsp; sends advanced remote control commands based on current zone; you may use "get avr remoteControl &lt;Get-command&gt; help" to see details about possible values and resulting readings. In Case the device does not support the command, just nothing happens as normally the device does not send any response. In case the command is temporarily not available you may see according feedback from the log file using attribute verbose=4.
  2898. </li>
  2899. <li>
  2900. <b>statusRequest</b> &nbsp;&nbsp;-&nbsp;&nbsp; clears cached settings and re-reads device XML configurations
  2901. </li>
  2902. </ul>
  2903. </ul><br>
  2904. <br>
  2905. <a name="ONKYO_AVRattr" id="ONKYO_AVRattr"></a> <b>Attributes</b>
  2906. <ul>
  2907. <ul>
  2908. <li>
  2909. <b>connectionCheck</b> &nbsp;&nbsp;1..120,off&nbsp;&nbsp; Pings the device every X seconds to verify connection status. Defaults to 60 seconds.
  2910. </li>
  2911. <li>
  2912. <b>inputs</b> &nbsp;&nbsp;-&nbsp;&nbsp; List of inputs, auto-generated after first connection to the device. Inputs may be deleted or re-ordered as required. To rename an input, one needs to put a comma behind the current name and enter the new name.
  2913. </li>
  2914. <li>
  2915. <b>model</b> &nbsp;&nbsp;-&nbsp;&nbsp; Contains the model name of the device. Cannot not be changed manually as it is going to be overwritten be the module.
  2916. </li>
  2917. <li>
  2918. <b>volumeSteps</b> &nbsp;&nbsp;-&nbsp;&nbsp; When using set commands volumeUp or volumeDown, the volume will be increased or decreased by these steps. Defaults to 1.
  2919. </li>
  2920. <li>
  2921. <b>volumeMax</b> &nbsp;&nbsp;1...100&nbsp;&nbsp; When set, any volume higher than this is going to be replaced by this value.
  2922. </li>
  2923. <li>
  2924. <b>wakeupCmd</b> &nbsp;&nbsp;-&nbsp;&nbsp; In case the device is unreachable and one is sending set command "on", this FHEM command will be executed before the actual "on" command is sent. E.g. may be used to turn on a switch before the device becomes available via network.
  2925. </li>
  2926. </ul>
  2927. </ul><br>
  2928. <br>
  2929. <b>Generated Readings/Events:</b><br>
  2930. <ul>
  2931. <li>
  2932. <b>audin_*</b> - Shows technical details about current audio input
  2933. </li>
  2934. <li>
  2935. <b>brand</b> - Shows brand name of the device manufacturer
  2936. </li>
  2937. <li>
  2938. <b>channel</b> - Shows current network service name when (e.g. streaming services like Spotify); part of FHEM-4-AV-Devices compatibility
  2939. </li>
  2940. <li>
  2941. <b>currentAlbum</b> - Shows current Album information; part of FHEM-4-AV-Devices compatibility
  2942. </li>
  2943. <li>
  2944. <b>currentArtist</b> - Shows current Artist information; part of FHEM-4-AV-Devices compatibility
  2945. </li>
  2946. <li>
  2947. <b>currentMedia</b> - currently no in use
  2948. </li>
  2949. <li>
  2950. <b>currentTitle</b> - Shows current Title information; part of FHEM-4-AV-Devices compatibility
  2951. </li>
  2952. <li>
  2953. <b>currentTrack*</b> - Shows current track timer information; part of FHEM-4-AV-Devices compatibility
  2954. </li>
  2955. <li>
  2956. <b>deviceid</b> - Shows device name as set in device settings
  2957. </li>
  2958. <li>
  2959. <b>deviceyear</b> - Shows model device year
  2960. </li>
  2961. <li>
  2962. <b>firmwareversion</b> - Shows current firmware version
  2963. </li>
  2964. <li>
  2965. <b>input</b> - Shows currently used input; part of FHEM-4-AV-Devices compatibility
  2966. </li>
  2967. <li>
  2968. <b>mute</b> - Reports the mute status of the device (can be "on" or "off")
  2969. </li>
  2970. <li>
  2971. <b>playStatus</b> - Shows current network service playback status; part of FHEM-4-AV-Devices compatibility
  2972. </li>
  2973. <li>
  2974. <b>power</b> - Reports the power status of the device (can be "on" or "off")
  2975. </li>
  2976. <li>
  2977. <b>presence</b> - Reports the presence status of the receiver (can be "absent" or "present"). In case of an absent device, control is not possible.
  2978. </li>
  2979. <li>
  2980. <b>repeat</b> - Shows current network service repeat status; part of FHEM-4-AV-Devices compatibility
  2981. </li>
  2982. <li>
  2983. <b>screen*</b> - Experimental: Gives some information about text that is being shown via on-screen menu
  2984. </li>
  2985. <li>
  2986. <b>shuffle</b> - Shows current network service shuffle status; part of FHEM-4-AV-Devices compatibility
  2987. </li>
  2988. <li>
  2989. <b>sleep</b> - Reports current sleep state (can be "off" or shows timer in minutes)
  2990. </li>
  2991. <li>
  2992. <b>state</b> - Reports current network connection status to the device
  2993. </li>
  2994. <li>
  2995. <b>stateAV</b> - Zone status from user perspective combining readings presence, power, mute and playStatus to a useful overall status.
  2996. </li>
  2997. <li>
  2998. <b>volume</b> - Reports current volume level of the receiver in percentage values (between 0 and 100 %)
  2999. </li>
  3000. <li>
  3001. <b>vidin_*</b> - Shows technical details about current video input before image processing
  3002. </li>
  3003. <li>
  3004. <b>vidout_*</b> - Shows technical details about current video output after image processing
  3005. </li>
  3006. <li>
  3007. <b>zones</b> - Shows total available zones of device
  3008. </li>
  3009. </ul>
  3010. <br>
  3011. Using remoteControl get-command might result in creating new readings in case the device sends any data.<br>
  3012. </ul>
  3013. =end html
  3014. =begin html_DE
  3015. <p>
  3016. <a name="ONKYO_AVR" id="ONKYO_AVR"></a>
  3017. </p>
  3018. <h3>
  3019. ONKYO_AVR
  3020. </h3>
  3021. <ul>
  3022. Eine deutsche Version der Dokumentation ist derzeit nicht vorhanden. Die englische Version ist hier zu finden:
  3023. </ul>
  3024. <ul>
  3025. <a href='http://fhem.de/commandref.html#ONKYO_AVR'>ONKYO_AVR</a>
  3026. </ul>
  3027. =end html_DE
  3028. =cut