70_ONKYO_AVR.pm 121 KB

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