70_ONKYO_AVR.pm 122 KB

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