98_SB_PLAYER.pm 226 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989
  1. # ##############################################################################
  2. # $Id: 98_SB_PLAYER.pm 12229 2016-10-01 16:46:46Z chrisd70 $
  3. #
  4. # FHEM Module for Squeezebox Players
  5. #
  6. # ##############################################################################
  7. #
  8. # used to interact with Squeezebox Player
  9. #
  10. # ##############################################################################
  11. #
  12. # Written by bugster_de
  13. #
  14. # Contributions from: Siggi85, Oliv06, ChrisD, Markus M., Matthew, KernSani,
  15. # Heppel, Eberhard
  16. #
  17. # ##############################################################################
  18. #
  19. # This is absolutley open source. Please feel free to use just as you
  20. # like. Please note, that no warranty is given and no liability
  21. # granted
  22. #
  23. # ##############################################################################
  24. #
  25. # we have the following readings
  26. # state on or off
  27. #
  28. # ##############################################################################
  29. #
  30. # we have the following attributes
  31. # timer the time frequency how often we check
  32. # volumeStep the volume delta when sending the up or down command
  33. # timeout the timeout in seconds for the TCP connection
  34. #
  35. # ##############################################################################
  36. # we have the following internals (all UPPERCASE)
  37. # PLAYERIP the IP adress of the player in the network
  38. # PLAYERID the unique identifier of the player. Mostly the MAC
  39. # SERVER based on the IP and the port as given
  40. # IP the IP of the server
  41. # PLAYERNAME the name of the Player
  42. # CONNECTION the connection status to the server
  43. # CANPOWEROFF is the player supporting power off commands
  44. # MODEL the model of the player
  45. # DISPLAYTYPE what sort of display is there, if any
  46. #
  47. # ##############################################################################
  48. # based on 98_SB_PLAYER.pm 9752 beta 0060 CD/MM/Matthew/Heppel
  49. # ##############################################################################
  50. package main;
  51. use strict;
  52. use warnings;
  53. use IO::Socket;
  54. use URI::Escape;
  55. use Encode qw(decode encode);
  56. # include this for the self-calling timer we use later on
  57. use Time::HiRes qw(gettimeofday);
  58. no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  59. use constant { true => 1, false => 0 };
  60. # the list of favorites
  61. # CD 0010 moved to $hash->{helper}{SB_PLAYER_Favs}, fixes problem on module reload
  62. #my %SB_PLAYER_Favs;
  63. # the list of sync masters
  64. # CD 0010 moved to $hash->{helper}{SB_PLAYER_SyncMasters}, fixes problem on module reload
  65. #my %SB_PLAYER_SyncMasters;
  66. # the list of Server side playlists
  67. # CD 0010 moved to $hash->{helper}{SB_PLAYER_Playlists}, fixes problem on module reload
  68. #my %SB_PLAYER_Playlists;
  69. # used for $hash->{helper}{ttsstate}
  70. use constant TTS_IDLE => 0;
  71. use constant TTS_TEXT2SPEECH_BUSY => 4;
  72. use constant TTS_TEXT2SPEECH_ACTIVE => 6;
  73. use constant TTS_WAITFORPOWERON => 10;
  74. use constant TTS_SAVE => 20;
  75. use constant TTS_UNSYNC => 30;
  76. use constant TTS_SETVOLUME => 40;
  77. use constant TTS_LOADPLAYLIST => 50;
  78. use constant TTS_DELAY => 55;
  79. use constant TTS_WAITFORPLAY => 60;
  80. use constant TTS_PLAYING => 70;
  81. use constant TTS_STOP => 80;
  82. use constant TTS_RESTORE => 90;
  83. use constant TTS_SYNC => 100;
  84. use constant TTS_SYNCGROUPACTIVE => 1000;
  85. use constant TTS_EXT_TEXT2SPEECH_BUSY => 2004;
  86. use constant TTS_EXT_TEXT2SPEECH_ACTIVE => 2006;
  87. my %ttsstates = ( 0 =>'idle',
  88. 4 =>'Text2Speech busy, waiting',
  89. 6 =>'Text2Speech active',
  90. 10 =>'wait for power on',
  91. 20 =>'save state',
  92. 30 =>'unsync player',
  93. 40 =>'set volume',
  94. 50 =>'load playlist',
  95. 55 =>'delay',
  96. 60 =>'wait for play',
  97. 70 =>'playing',
  98. 80 =>'stopped',
  99. 90 =>'restore state',
  100. 100 =>'sync',
  101. 1000=>'active',
  102. 2004=>'Text2Speech busy, waiting',
  103. 2006=>'Text2Speech active');
  104. my $SB_PLAYER_hasDataDumper = 1; # CD 0036
  105. # ----------------------------------------------------------------------------
  106. # Initialisation routine called upon start-up of FHEM
  107. # ----------------------------------------------------------------------------
  108. sub SB_PLAYER_Initialize( $ ) {
  109. my ($hash) = @_;
  110. # the commands we provide to FHEM
  111. # installs the respecitive call-backs for FHEM. The call back in quotes
  112. # must be realised as a sub later on in the file
  113. $hash->{DefFn} = "SB_PLAYER_Define";
  114. $hash->{UndefFn} = "SB_PLAYER_Undef";
  115. $hash->{ShutdownFn} = "SB_PLAYER_Shutdown";
  116. $hash->{SetFn} = "SB_PLAYER_Set";
  117. $hash->{GetFn} = "SB_PLAYER_Get";
  118. # for the two step approach
  119. $hash->{Match} = "^SB_PLAYER:";
  120. $hash->{ParseFn} = "SB_PLAYER_Parse";
  121. # CD 0007
  122. $hash->{AttrFn} = "SB_PLAYER_Attr";
  123. # CD 0032
  124. $hash->{NotifyFn} = "SB_PLAYER_Notify";
  125. # the attributes we have. Space separated list of attribute values in
  126. # the form name:default1,default2
  127. $hash->{AttrList} = "IODev ignore:1,0 do_not_notify:1,0 ";
  128. $hash->{AttrList} .= "volumeStep volumeLimit ";
  129. $hash->{AttrList} .= "ttslanguage:de,en,fr ttslink ";
  130. $hash->{AttrList} .= "donotnotify:true,false ";
  131. $hash->{AttrList} .= "idismac:true,false ";
  132. $hash->{AttrList} .= "serverautoon:true,false ";
  133. $hash->{AttrList} .= "fadeinsecs ";
  134. $hash->{AttrList} .= "amplifier:on,play ";
  135. $hash->{AttrList} .= "coverartheight:50,100,200 ";
  136. $hash->{AttrList} .= "coverartwidth:50,100,200 ";
  137. # CD 0028
  138. $hash->{AttrList} .= "ttsVolume ";
  139. $hash->{AttrList} .= "ttsOptions ";
  140. # CD 0030
  141. $hash->{AttrList} .= "ttsDelay ";
  142. # CD 0032
  143. $hash->{AttrList} .= "ttsPrefix "; # DJAlex 665
  144. # CD 0033
  145. $hash->{AttrList} .= "ttsMP3FileDir ";
  146. $hash->{AttrList} .= "ttsAPIKey "; # CD 0045
  147. # CD 0007
  148. $hash->{AttrList} .= "syncVolume ";
  149. $hash->{AttrList} .= "amplifierDelayOff "; # CD 0012
  150. $hash->{AttrList} .= "updateReadingsOnSet:true,false "; # CD 0017
  151. $hash->{AttrList} .= "statusRequestInterval "; # CD 0037
  152. $hash->{AttrList} .= "syncedNamesSource:LMS,FHEM "; # CD 0055
  153. $hash->{AttrList} .= $readingFnAttributes;
  154. # CD 0036 aus 37_sonosBookmarker
  155. eval "use Data::Dumper";
  156. $SB_PLAYER_hasDataDumper = 0 if($@);
  157. }
  158. # CD 0007 start
  159. # ----------------------------------------------------------------------------
  160. # Attr functions
  161. # ----------------------------------------------------------------------------
  162. sub SB_PLAYER_Attr( @ ) {
  163. my $cmd = shift( @_ );
  164. my $name = shift( @_ );
  165. my @args = @_;
  166. my $hash = $defs{$name};
  167. Log( 4, "SB_PLAYER_Attr($name): called with @args" );
  168. if( $args[ 0 ] eq "syncVolume" ) {
  169. if( $cmd eq "set" ) {
  170. if (defined($args[1])) {
  171. if($args[1] eq "1") {
  172. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume 1\n" );
  173. } else {
  174. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume 0\n" );
  175. }
  176. } else {
  177. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume ?\n" );
  178. }
  179. } else {
  180. }
  181. }
  182. # CD 0012 start - bei Änderung des Attributes Zustand überprüfen
  183. elsif( $args[ 0 ] eq "amplifier" ) {
  184. RemoveInternalTimer( "DelayAmplifier:$name");
  185. InternalTimer( gettimeofday() + 0.01,
  186. "SB_PLAYER_tcb_DelayAmplifier", # CD 0014 Name geändert
  187. "DelayAmplifier:$name",
  188. 0 );
  189. }
  190. # CD 0043 start
  191. elsif( $args[ 0 ] eq "amplifierDelayOff" ) {
  192. $hash->{helper}{amplifierDelayOffStop}=0;
  193. $hash->{helper}{amplifierDelayOffPower}=0;
  194. $hash->{helper}{amplifierDelayOffPause}=0;
  195. if( $cmd eq "set" ) {
  196. if (defined($args[1])) {
  197. my @delays=split(',',$args[1]);
  198. $hash->{helper}{amplifierDelayOffStop}=$delays[0]+0 if defined($delays[0]);
  199. $hash->{helper}{amplifierDelayOffPower}=$delays[0]+0 if defined($delays[0]);
  200. $hash->{helper}{amplifierDelayOffPause}=$delays[1]+0 if defined($delays[1]);
  201. } else {
  202. return "missing value for amplifierDelayOff";
  203. }
  204. } else {
  205. }
  206. }
  207. # CD 0043 end
  208. # CD 0028
  209. elsif( $args[ 0 ] eq "ttsVolume" ) {
  210. if( $cmd eq "set" ) {
  211. if (defined($args[1])) {
  212. return "invalid value for ttsVolume" if(($args[1] < 0)||($args[1] > 100));
  213. $hash->{helper}{ttsVolume}=$args[1];
  214. } else {
  215. return "invalid value for ttsVolume";
  216. }
  217. } else {
  218. delete($hash->{helper}{ttsVolume}) if(defined($hash->{helper}{ttsVolume}));
  219. }
  220. }
  221. elsif( $args[ 0 ] eq "ttsOptions" ) {
  222. if( $cmd eq "set" ) {
  223. if (defined($args[1])) {
  224. my @options=split(',',$args[1]);
  225. delete($hash->{helper}{ttsOptions}) if(defined($hash->{helper}{ttsOptions}));
  226. for my $opt (@options) {
  227. $hash->{helper}{ttsOptions}{debug}=1 if($opt=~ m/debug/);
  228. $hash->{helper}{ttsOptions}{debugsaverestore}=1 if($opt=~ m/debugsaverestore/); # CD 0029
  229. $hash->{helper}{ttsOptions}{unsync}=1 if($opt=~ m/unsync/);
  230. $hash->{helper}{ttsOptions}{nosaverestore}=1 if($opt=~ m/nosaverestore/);
  231. $hash->{helper}{ttsOptions}{forcegroupon}=1 if($opt=~ m/forcegroupon/);
  232. $hash->{helper}{ttsOptions}{internalsave}=1 if($opt=~ m/internalsave/); # CD 0029
  233. $hash->{helper}{ttsOptions}{ignorevolumelimit}=1 if($opt=~ m/ignorevolumelimit/); # CD 0031
  234. $hash->{helper}{ttsOptions}{doubleescape}=1 if($opt=~ m/doubleescape/); # CD 0059
  235. }
  236. } else {
  237. return "invalid value for ttsOptions";
  238. }
  239. } else {
  240. delete($hash->{helper}{ttsOptions}) if(defined($hash->{helper}{ttsOptions}));
  241. }
  242. }
  243. # CD 0030
  244. elsif( $args[ 0 ] eq "ttsDelay" ) {
  245. if( $cmd eq "set" ) {
  246. if (defined($args[1])) {
  247. my @options=split(',',$args[1]);
  248. $hash->{helper}{ttsDelay}{PowerIsOn}=$options[0];
  249. if(defined($options[1])) {
  250. $hash->{helper}{ttsDelay}{PowerIsOff}=$options[1];
  251. } else {
  252. $hash->{helper}{ttsDelay}{PowerIsOff}=$options[0];
  253. }
  254. } else {
  255. return "invalid value for ttsDelay";
  256. }
  257. } else {
  258. delete($hash->{helper}{ttsDelay}) if(defined($hash->{helper}{ttsDelay}));
  259. }
  260. }
  261. # CD 0037
  262. elsif( $args[ 0 ] eq "statusRequestInterval" ) {
  263. RemoveInternalTimer( $hash );
  264. if( $cmd eq "set" ) {
  265. if (defined($args[1])) {
  266. InternalTimer( gettimeofday() + $args[1],
  267. "SB_PLAYER_GetStatus",
  268. $hash,
  269. 0 ) if $args[1]>0;
  270. } else {
  271. return "missing value for statusRequestInterval";
  272. }
  273. } else {
  274. InternalTimer( gettimeofday() + 5,
  275. "SB_PLAYER_GetStatus",
  276. $hash,
  277. 0 );
  278. }
  279. }
  280. # CD 0055 start
  281. elsif( $args[ 0 ] eq "syncedNamesSource" ) {
  282. IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ) if ($init_done>0);
  283. }
  284. # CD 0055 end
  285. return;
  286. # CD 0012
  287. }
  288. # CD 0007 end
  289. # ----------------------------------------------------------------------------
  290. # Definition of a module instance
  291. # called when defining an element via fhem.cfg
  292. # ----------------------------------------------------------------------------
  293. sub SB_PLAYER_Define( $$ ) {
  294. my ( $hash, $def ) = @_;
  295. my $name = $hash->{NAME};
  296. my @a = split("[ \t][ \t]*", $def);
  297. # do we have the right number of arguments?
  298. if( ( @a < 3 ) || ( @a > 5 ) ) {
  299. Log3( $hash, 1, "SB_PLAYER_Define: falsche Anzahl an Argumenten" );
  300. return( "wrong syntax: define <name> SB_PLAYER <playerid> " .
  301. "<ampl:FHEM_NAME> <coverart:FHEMNAME>" );
  302. }
  303. # remove the name and our type
  304. # my $name = shift( @a );
  305. shift( @a ); # name
  306. shift( @a ); # type
  307. # needed for manual creation of the Player; autocreate checks in ParseFn
  308. if( SB_PLAYER_IsValidMAC( $a[ 0] ) == 1 ) {
  309. # the MAC adress is valid
  310. $hash->{PLAYERMAC} = lc($a[ 0 ]); # CD 0026 lc added
  311. } else {
  312. my $msg = "SB_PLAYER_Define: playerid ist keine MAC Adresse " .
  313. "im Format xx:xx:xx:xx:xx:xx oder xx-xx-xx-xx-xx-xx";
  314. Log3( $hash, 1, $msg );
  315. return( $msg );
  316. }
  317. # shift the MAC away
  318. shift( @a );
  319. $hash->{AMPLIFIER} = "none";
  320. $hash->{COVERARTLINK} = "none";
  321. foreach( @a ) {
  322. if( $_ =~ /^(ampl:)(.*)/ ) {
  323. $hash->{AMPLIFIER} = $2;
  324. next;
  325. } elsif( $_ =~ /^(coverart:)(.*)/ ) {
  326. $hash->{COVERARTLINK} = $2;
  327. next;
  328. } else {
  329. next;
  330. }
  331. }
  332. Log3( $hash, 5, "SB_PLAYER_Define successfully called" );
  333. # remove the : from the ID
  334. my @idbuf = split( ":", $hash->{PLAYERMAC} );
  335. my $uniqueid = join( "", @idbuf );
  336. # our unique id
  337. $hash->{FHEMUID} = $uniqueid;
  338. # do the alarms fade in
  339. #$hash->{ALARMSFADEIN} = "?"; # CD 0016 deaktiviert, -> Reading
  340. # the number of alarms of the player
  341. $hash->{helper}{ALARMSCOUNT} = 0; # CD 0016 ALARMSCOUNT nach {helper} verschoben
  342. # for the two step approach
  343. $modules{SB_PLAYER}{defptr}{$uniqueid} = $hash;
  344. AssignIoPort( $hash );
  345. if (!defined($hash->{OLDDEF})) { # CD 0044
  346. # preset the internals
  347. # can the player power off
  348. $hash->{CANPOWEROFF} = "?";
  349. # graphical or textual display
  350. $hash->{DISPLAYTYPE} = "?";
  351. # which model do we see?
  352. $hash->{MODEL} = "?";
  353. # what's the ip adress of the player
  354. $hash->{PLAYERIP} = "?";
  355. # the name of the player as assigned by the server
  356. $hash->{PLAYERNAME} = "?";
  357. # the last alarm we did set
  358. $hash->{LASTALARM} = 1;
  359. # the reference to the favorites list
  360. $hash->{FAVREF} = " ";
  361. # the command for selecting a favorite
  362. $hash->{FAVSET} = "favorites";
  363. # the entry in the global hash table
  364. $hash->{FAVSTR} = "not,yet,defined ";
  365. # the selected favorites
  366. $hash->{FAVSELECT} = "not";
  367. # last received answer from the server
  368. $hash->{LASTANSWER} = "none";
  369. # for sync group (multi-room)
  370. $hash->{SYNCMASTER} = "?";
  371. $hash->{SYNCGROUP} = "?";
  372. $hash->{SYNCED} = "?";
  373. # seconds until sleeping
  374. $hash->{WILLSLEEPIN} = "?";
  375. # the list of potential sync masters
  376. $hash->{SYNCMASTERS} = "not,yet,defined";
  377. # is currently playing a remote stream
  378. $hash->{ISREMOTESTREAM} = "?";
  379. # the server side playlists
  380. $hash->{SERVERPLAYLISTS} = "not,yet,defined";
  381. # the URL to the artwork
  382. $hash->{ARTWORKURL} = "?";
  383. $hash->{COVERARTURL} = "?";
  384. $hash->{COVERID} = "?";
  385. # the IP and Port of the Server
  386. $hash->{SBSERVER} = "?";
  387. }
  388. # preset the attributes
  389. # volume delta settings
  390. if( !defined( $attr{$name}{volumeStep} ) ) {
  391. $attr{$name}{volumeStep} = 10;
  392. }
  393. # Upper limit for volume setting
  394. if( !defined( $attr{$name}{volumeLimit} ) ) {
  395. $attr{$name}{volumeLimit} = 100;
  396. }
  397. # how many secs for fade in when going from stop to play
  398. if( !defined( $attr{$name}{fadeinsecs} ) ) {
  399. $attr{$name}{fadeinsecs} = "10";
  400. }
  401. # do not create FHEM notifies (true=no notifies)
  402. if( !defined( $attr{$name}{donotnotify} ) ) {
  403. $attr{$name}{donotnotify} = "true";
  404. }
  405. # is the ID the MAC adress
  406. if( !defined( $attr{$name}{idismac} ) ) {
  407. $attr{$name}{idismac} = "true";
  408. }
  409. # the language for text2speech
  410. if( !defined( $attr{$name}{ttslanguage} ) ) {
  411. $attr{$name}{ttslanguage} = "de";
  412. }
  413. # link to the text2speech engine
  414. if( !defined( $attr{$name}{ttslink} ) ) {
  415. $attr{$name}{ttslink} = "http://translate.google.com" .
  416. "/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob"; # CD 0045 Format geändert, &client=t&prev=input hinzugefügt, CD 0048 client=tw-ob verwenden
  417. }
  418. # turn on the server when player is used
  419. if( !defined( $attr{$name}{serverautoon} ) ) {
  420. $attr{$name}{serverautoon} = "true";
  421. }
  422. # amplifier on/off when play/pause or on/off
  423. if( !defined( $attr{$name}{amplifier} ) ) {
  424. $attr{$name}{amplifier} = "play";
  425. }
  426. # height and width of the cover art for the URL
  427. if( !defined( $attr{$name}{coverartwidth} ) ) {
  428. $attr{$name}{coverartwidth} = 50;
  429. }
  430. if( !defined( $attr{$name}{coverartheight} ) ) {
  431. $attr{$name}{coverartheight} = 50;
  432. }
  433. # Preset our readings if undefined
  434. my $tn = TimeNow();
  435. # according to development guidelines of FHEM AV Module
  436. if( !defined( $hash->{READINGS}{presence}{VAL} ) ) {
  437. $hash->{READINGS}{presence}{VAL} = "?";
  438. $hash->{READINGS}{presence}{TIME} = $tn;
  439. }
  440. # according to development guidelines of FHEM AV Module
  441. if( !defined( $hash->{READINGS}{power}{VAL} ) ) {
  442. $hash->{READINGS}{power}{VAL} = "?";
  443. $hash->{READINGS}{power}{TIME} = $tn;
  444. }
  445. # the last unkown command
  446. if( !defined( $hash->{READINGS}{lastunknowncmd}{VAL} ) ) {
  447. $hash->{READINGS}{lastunknowncmd}{VAL} = "none";
  448. $hash->{READINGS}{lastunknowncmd}{TIME} = $tn;
  449. }
  450. # the last unkown IR command
  451. if( !defined( $hash->{READINGS}{lastir}{VAL} ) ) {
  452. $hash->{READINGS}{lastir}{VAL} = "?";
  453. $hash->{READINGS}{lastir}{TIME} = $tn;
  454. }
  455. # the id of the alarm we create # CD 0015 deaktiviert
  456. # if( !defined( $hash->{READINGS}{alarmid1}{VAL} ) ) {
  457. # $hash->{READINGS}{alarmid1}{VAL} = "none";
  458. # $hash->{READINGS}{alarmid1}{TIME} = $tn;
  459. # }
  460. # if( !defined( $hash->{READINGS}{alarmid2}{VAL} ) ) {
  461. # $hash->{READINGS}{alarmid2}{VAL} = "none";
  462. # $hash->{READINGS}{alarmid2}{TIME} = $tn;
  463. # }
  464. # values according to standard
  465. if( !defined( $hash->{READINGS}{playStatus}{VAL} ) ) {
  466. $hash->{READINGS}{playStatus}{VAL} = "?";
  467. $hash->{READINGS}{playStatus}{TIME} = $tn;
  468. }
  469. if( !defined( $hash->{READINGS}{currentArtist}{VAL} ) ) {
  470. $hash->{READINGS}{currentArtist}{VAL} = "?";
  471. $hash->{READINGS}{currentArtist}{TIME} = $tn;
  472. }
  473. if( !defined( $hash->{READINGS}{currentAlbum}{VAL} ) ) {
  474. $hash->{READINGS}{currentAlbum}{VAL} = "?";
  475. $hash->{READINGS}{currentAlbum}{TIME} = $tn;
  476. }
  477. if( !defined( $hash->{READINGS}{currentTitle}{VAL} ) ) {
  478. $hash->{READINGS}{currentTitle}{VAL} = "?";
  479. $hash->{READINGS}{currentTitle}{TIME} = $tn;
  480. }
  481. if( !defined( $hash->{READINGS}{favorites}{VAL} ) ) {
  482. $hash->{READINGS}{favorites}{VAL} = "not";
  483. $hash->{READINGS}{favorites}{TIME} = $tn;
  484. }
  485. if( !defined( $hash->{READINGS}{playlists}{VAL} ) ) {
  486. $hash->{READINGS}{playlists}{VAL} = "not";
  487. $hash->{READINGS}{playlists}{TIME} = $tn;
  488. }
  489. # for the FHEM AV Development Guidelinses
  490. # we use this to store the currently playing ID to later on return to
  491. if( !defined( $hash->{READINGS}{currentMedia}{VAL} ) ) {
  492. $hash->{READINGS}{currentMedia}{VAL} = "?";
  493. $hash->{READINGS}{currentMedia}{TIME} = $tn;
  494. }
  495. if( !defined( $hash->{READINGS}{currentPlaylistName}{VAL} ) ) {
  496. $hash->{READINGS}{currentPlaylistName}{VAL} = "?";
  497. $hash->{READINGS}{currentPlaylistName}{TIME} = $tn;
  498. }
  499. if( !defined( $hash->{READINGS}{currentPlaylistUrl}{VAL} ) ) {
  500. $hash->{READINGS}{currentPlaylistUrl}{VAL} = "?";
  501. $hash->{READINGS}{currentPlaylistUrl}{TIME} = $tn;
  502. }
  503. if( !defined( $hash->{READINGS}{volume}{VAL} ) ) {
  504. $hash->{READINGS}{volume}{VAL} = 0;
  505. $hash->{READINGS}{volume}{TIME} = $tn;
  506. }
  507. if( !defined( $hash->{READINGS}{volumeStraight}{VAL} ) ) {
  508. $hash->{READINGS}{volumeStraight}{VAL} = "?";
  509. $hash->{READINGS}{volumeStraight}{TIME} = $tn;
  510. }
  511. if( !defined( $hash->{READINGS}{connected}{VAL} ) ) {
  512. $hash->{READINGS}{connected}{VAL} = "?";
  513. $hash->{READINGS}{connected}{TIME} = $tn;
  514. }
  515. if( !defined( $hash->{READINGS}{signalstrength}{VAL} ) ) {
  516. $hash->{READINGS}{signalstrength}{VAL} = "?";
  517. $hash->{READINGS}{currentTitle}{TIME} = $tn;
  518. }
  519. if( !defined( $hash->{READINGS}{shuffle}{VAL} ) ) {
  520. $hash->{READINGS}{shuffle}{VAL} = "?";
  521. $hash->{READINGS}{currentTitle}{TIME} = $tn;
  522. }
  523. if( !defined( $hash->{READINGS}{repeat}{VAL} ) ) {
  524. $hash->{READINGS}{repeat}{VAL} = "?";
  525. $hash->{READINGS}{currentTitle}{TIME} = $tn;
  526. }
  527. if( !defined( $hash->{READINGS}{state}{VAL} ) ) {
  528. $hash->{READINGS}{state}{VAL} = "?";
  529. $hash->{READINGS}{state}{TIME} = $tn;
  530. }
  531. # mrbreil 0047 start
  532. if( !defined( $hash->{READINGS}{currentTrackPosition}{VAL} ) ) {
  533. $hash->{READINGS}{currentTrackPosition}{VAL} = 0;
  534. $hash->{READINGS}{currentTrackPosition}{TIME} = $tn;
  535. }
  536. # mrbreil 0047 end
  537. $hash->{helper}{ttsstate}=TTS_IDLE; # CD 0028
  538. if (!defined($hash->{OLDDEF})) { # CD 0044
  539. SB_PLAYER_LoadPlayerStates($hash) if($SB_PLAYER_hasDataDumper==1); # CD 0036
  540. $hash->{helper}{playerStatusOK}=0; # CD 0042
  541. $hash->{helper}{playerStatusOKCounter}=0; # CD 0042
  542. $hash->{helper}{amplifierDelayOffStop}=0; # CD 0043
  543. $hash->{helper}{amplifierDelayOffPower}=0; # CD 0043
  544. $hash->{helper}{amplifierDelayOffPause}=0; # CD 0043
  545. }
  546. # do and update of the status
  547. InternalTimer( gettimeofday() + 10,
  548. "SB_PLAYER_GetStatus",
  549. $hash,
  550. 0 );
  551. return( undef );
  552. }
  553. # CD 0002 start
  554. sub SB_PLAYER_tcb_QueryCoverArt($) { # CD 0014 Name geändert
  555. my($in ) = shift;
  556. my(undef,$name) = split(':',$in);
  557. my $hash = $defs{$name};
  558. #Log 0,"delayed cover art query";
  559. IOWrite( $hash, "$hash->{PLAYERMAC} status - 1 tags:Kcu\n" ); # CD 0030 u added to tags
  560. # CD 0005 query cover art for synced players
  561. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  562. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) { # CD 0018 none hinzugefügt
  563. my @pl=split(",",$hash->{SYNCGROUP});
  564. foreach (@pl) {
  565. IOWrite( $hash, "$_ status - 1 tags:Kcu\n" ); # CD 0039 u hinzugefügt
  566. }
  567. }
  568. }
  569. }
  570. # CD 0002 end
  571. # CD 0014 start
  572. sub SB_PLAYER_tcb_DeleteRecallPause($) {
  573. my($in ) = shift;
  574. my(undef,$name) = split(':',$in);
  575. my $hash = $defs{$name};
  576. delete($hash->{helper}{recallPause});
  577. }
  578. sub SB_PLAYER_QueryElapsedTime($) {
  579. my ($hash) = @_;
  580. if(!defined($hash->{helper}{lastTimeQuery})||($hash->{helper}{lastTimeQuery}<gettimeofday()-5)) {
  581. #Log 0,"Querying time, last: $hash->{helper}{lastTimeQuery}, now: ".gettimeofday();
  582. $hash->{helper}{lastTimeQuery}=gettimeofday();
  583. IOWrite( $hash, "$hash->{PLAYERMAC} time ?\n" );
  584. }
  585. }
  586. # CD 0014 end
  587. # CD 0047 start
  588. sub SB_PLAYER_tcb_QueryElapsedTime( $ ) {
  589. my($in ) = shift;
  590. my(undef,$name) = split(':',$in);
  591. my $hash = $defs{$name};
  592. SB_PLAYER_QueryElapsedTime($hash);
  593. RemoveInternalTimer( "QueryElapsedTime:$name");
  594. if (ReadingsVal($name,"playStatus","x") eq "playing") {
  595. InternalTimer( gettimeofday() + 5,
  596. "SB_PLAYER_tcb_QueryElapsedTime",
  597. "QueryElapsedTime:$name",
  598. 0 );
  599. }
  600. }
  601. # CD 0047 end
  602. # CD 0028 start
  603. sub SB_PLAYER_tcb_TTSRestore( $ ) {
  604. my($in ) = shift;
  605. my(undef,$name) = split(':',$in);
  606. my $hash = $defs{$name};
  607. # CD 0033 start
  608. if(defined($hash->{helper}{ttsqueue})) {
  609. SB_PLAYER_SetTTSState($hash,TTS_LOADPLAYLIST,0,0);
  610. SB_PLAYER_LoadTalk($hash);
  611. } else {
  612. # CD 0033 end
  613. if(!defined($hash->{helper}{ttsOptions}{nosaverestore})) {
  614. SB_PLAYER_SetTTSState($hash,TTS_RESTORE,0,0);
  615. SB_PLAYER_Recall( $hash, "xxTTSxx del" );
  616. } else {
  617. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  618. }
  619. }
  620. }
  621. # CD 0028 end
  622. # ----------------------------------------------------------------------------
  623. # called from the global dispatch if new data is available
  624. # ----------------------------------------------------------------------------
  625. sub SB_PLAYER_Parse( $$ ) {
  626. my ( $iohash, $msg ) = @_;
  627. # we expect the data to be in the following format
  628. # xxxxxxxxxxxx cmd1 cmd2 cmd3 ...
  629. # where xxxxxxxxxxxx is derived from xx:xx:xx:xx:xx:xx
  630. # that needs to be done by the server
  631. Log3( $iohash, 5, "SB_PLAYER_Parse: called with $msg" );
  632. # storing the last in an array is necessery, for tagged responses
  633. my ( $modtype, $id, @data ) = split(":", $msg, 3 );
  634. Log3( $iohash, 5, "SB_PLAYER_Parse: type:$modtype, ID:$id CMD:@data" );
  635. if( $modtype ne "SB_PLAYER" ) {
  636. # funny stuff happens at the disptach function
  637. Log3( $iohash, 5, "SB_PLAYER_Parse: wrong type given." );
  638. }
  639. # let's see what we got. Split the data at the space
  640. # necessery, for tagged responses
  641. my @args = split( " ", join( " ", @data ) );
  642. my $cmd = shift( @args );
  643. my $hash = $modules{SB_PLAYER}{defptr}{$id};
  644. if( !$hash ) {
  645. Log3( undef, 3, "SB_PLAYER Unknown device with ID $id, " .
  646. "please define it");
  647. # do the autocreate; derive the unique id (MAC adress)
  648. my @playermac = ( $id =~ m/.{2}/g );
  649. my $idbuf = join( ":", @playermac );
  650. Log3( undef, 3, "SB_PLAYER Dervived the following MAC $idbuf " );
  651. if( SB_PLAYER_IsValidMAC( $idbuf ) == 1 ) {
  652. # the MAC Adress is valid
  653. Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is a valid " .
  654. "MAC Adress" );
  655. # this line supports autocreate
  656. return( "UNDEFINED SB_PLAYER_$id SB_PLAYER $idbuf" );
  657. } else {
  658. # the MAC adress is not valid
  659. Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is NOT " .
  660. "a valid MAC Adress" );
  661. return( undef );
  662. }
  663. }
  664. # so the data is for us
  665. my $name = $hash->{NAME};
  666. #return "" if(IsIgnored($name));
  667. Log3( $hash, 5, "SB_PLAYER_Parse: $name CMD:$cmd ARGS:@args..." );
  668. # what ever we have received, signal it
  669. $hash->{LASTANSWER} = "$cmd @args";
  670. $hash->{helper}{ttsstate}=TTS_IDLE if(!defined($hash->{helper}{ttsstate})); # CD 0028
  671. # signal the update to FHEM
  672. readingsBeginUpdate( $hash );
  673. if( $cmd eq "mixer" ) {
  674. if( $args[ 0 ] eq "volume" ) {
  675. # update the volume
  676. if ($args[ 1 ] eq "?") {
  677. # it is a request
  678. } else {
  679. # CD 0040 start
  680. if( ( index( $args[ 1 ], "+" ) != -1 ) || ( index( $args[ 1 ], "-" ) != -1 ) ) {
  681. # that was a relative value. We do nothing and fire an update
  682. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ?\n" );
  683. } else {
  684. # CD 0040 end
  685. SB_PLAYER_UpdateVolumeReadings( $hash, $args[ 1 ], true );
  686. # CD 0007 start
  687. if((defined($hash->{helper}{setSyncVolume}) && ($hash->{helper}{setSyncVolume} != $args[ 1 ]))|| (!defined($hash->{helper}{setSyncVolume}))) {
  688. SB_PLAYER_SetSyncedVolume($hash,$args[ 1 ]);
  689. }
  690. delete $hash->{helper}{setSyncVolume};
  691. # CD 0007 end
  692. }
  693. }
  694. }
  695. } elsif( $cmd eq "remote" ) {
  696. if( defined( $args[ 0 ] ) ) {
  697. $hash->{ISREMOTESTREAM} = "$args[ 0 ]";
  698. } else {
  699. $hash->{ISREMOTESTREAM} = "0";
  700. }
  701. } elsif( $cmd eq "play" ) {
  702. if(!defined($hash->{helper}{recallPause})) { # CD 0014
  703. readingsBulkUpdate( $hash, "playStatus", "playing" );
  704. SB_PLAYER_Amplifier( $hash );
  705. } # CD 0014
  706. } elsif( $cmd eq "stop" ) {
  707. readingsBulkUpdate( $hash, "playStatus", "stopped" );
  708. SB_PLAYER_Amplifier( $hash );
  709. } elsif( $cmd eq "pause" ) {
  710. if((defined($args[ 0 ])) && ( $args[ 0 ] eq "0" )) { # CD 0028 check if $args[0] exists
  711. readingsBulkUpdate( $hash, "playStatus", "playing" );
  712. SB_PLAYER_Amplifier( $hash );
  713. } else {
  714. readingsBulkUpdate( $hash, "playStatus", "paused" );
  715. SB_PLAYER_Amplifier( $hash );
  716. }
  717. } elsif( $cmd eq "mode" ) {
  718. my $updateSyncedPlayers=0; # CD 0039
  719. # a little more complex to fulfill FHEM Development guidelines
  720. Log3( $hash, 5, "SB_PLAYER_Parse($name): mode:$cmd args:$args[0]" );
  721. if( $args[ 0 ] eq "play" ) {
  722. # CD 0014 start
  723. if(defined($hash->{helper}{recallPause})) {
  724. IOWrite( $hash, "$hash->{PLAYERMAC} pause 1\n" );
  725. } else {
  726. # CD 0014 end
  727. readingsBulkUpdate( $hash, "playStatus", "playing" );
  728. SB_PLAYER_Amplifier( $hash );
  729. SB_PLAYER_QueryElapsedTime( $hash ); # CD 0014
  730. # CD 0047 start
  731. RemoveInternalTimer( "QueryElapsedTime:$name");
  732. InternalTimer( gettimeofday() + 5,
  733. "SB_PLAYER_tcb_QueryElapsedTime",
  734. "QueryElapsedTime:$name",
  735. 0 );
  736. # CD 0047 end
  737. } # CD 0014
  738. # CD 0029 start
  739. if(defined($hash->{helper}{ttsOptions}{logplay})) {
  740. Log3( $hash, 0, "SB_PLAYER_Parse: $name: mode play");
  741. delete($hash->{helper}{ttsOptions}{logplay});
  742. }
  743. # CD 0029
  744. # CD 0028 start
  745. if($hash->{helper}{ttsstate}==TTS_WAITFORPLAY) {
  746. SB_PLAYER_SetTTSState($hash,TTS_PLAYING,1,0);
  747. }
  748. if(($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) && ($hash->{SYNCMASTER} eq $hash->{PLAYERMAC})) {
  749. IOWrite( $hash, $hash->{helper}{ttsMaster} . " fhemrelay ttsplaying\n" );
  750. }
  751. # CD 0028 end
  752. $updateSyncedPlayers=1; # CD 0039 gesyncte Player aktualisieren
  753. } elsif( $args[ 0 ] eq "stop" ) {
  754. # CD 0028 start
  755. if($hash->{helper}{ttsstate}==TTS_PLAYING) {
  756. SB_PLAYER_TTSStopped($hash);
  757. }
  758. # wenn tts auf Slave aktiv ist schickt der LMS den Stop nur an den Master
  759. if (ReadingsVal($name,"presence","x") eq "present") { # CD 0056 Meldung nicht weiterschicken wenn der Master nicht 'present' ist
  760. if(($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) && ($hash->{SYNCMASTER} eq $hash->{PLAYERMAC})) {
  761. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  762. my @pl=split(",",$hash->{SYNCGROUP});
  763. foreach (@pl) {
  764. if ($hash->{PLAYERMAC} ne $_) {
  765. IOWrite( $hash, "$_ fhemrelay ttsstopped\n" );
  766. }
  767. }
  768. }
  769. }
  770. }
  771. # CD 0028 end
  772. readingsBulkUpdate( $hash, "playStatus", "stopped" );
  773. SB_PLAYER_Amplifier( $hash );
  774. RemoveInternalTimer( "QueryElapsedTime:$name"); # CD 0047
  775. readingsBulkUpdate( $hash, "currentTrackPosition", 0 ); # CD 0047
  776. $updateSyncedPlayers=1; # CD 0039 gesyncte Player aktualisieren
  777. } elsif( $args[ 0 ] eq "pause" ) {
  778. readingsBulkUpdate( $hash, "playStatus", "paused" );
  779. SB_PLAYER_Amplifier( $hash );
  780. $updateSyncedPlayers=1; # CD 0039 gesyncte Player aktualisieren
  781. } else {
  782. readingsBulkUpdate( $hash, "playStatus", $args[ 0 ] );
  783. }
  784. # CD 0039 gesyncte Player aktualisieren
  785. if($updateSyncedPlayers==1) {
  786. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  787. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  788. my @pl=split(",",$hash->{SYNCGROUP});
  789. foreach (@pl) {
  790. if ($hash->{PLAYERMAC} ne $_) {
  791. IOWrite( $hash, "$_ mode ?\n" );
  792. }
  793. }
  794. }
  795. }
  796. }
  797. # CD 0039 end
  798. } elsif( $cmd eq "newmetadata" ) {
  799. # the song has changed, but we are easy and just ask the player
  800. # sending the requests causes endless loop
  801. #IOWrite( $hash, "$hash->{PLAYERMAC} artist ?\n" );
  802. #IOWrite( $hash, "$hash->{PLAYERMAC} album ?\n" );
  803. #IOWrite( $hash, "$hash->{PLAYERMAC} title ?\n" );
  804. IOWrite( $hash, "$hash->{PLAYERMAC} remote ?\n" );
  805. #IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kc\n" );
  806. #SB_PLAYER_CoverArt( $hash ); # CD 0026 deaktiviert
  807. } elsif( $cmd eq "playlist" ) {
  808. my $queryMode=1; # CD 0014
  809. if( $args[ 0 ] eq "newsong" ) {
  810. # the song has changed, but we are easy and just ask the player
  811. IOWrite( $hash, "$hash->{PLAYERMAC} artist ?\n" );
  812. IOWrite( $hash, "$hash->{PLAYERMAC} album ?\n" );
  813. IOWrite( $hash, "$hash->{PLAYERMAC} title ?\n" );
  814. # CD 0007 get playlist name
  815. IOWrite( $hash, "$hash->{PLAYERMAC} playlist name ?\n" );
  816. # CD 0014 get duration and index
  817. IOWrite( $hash, "$hash->{PLAYERMAC} duration ?\n" );
  818. IOWrite( $hash, "$hash->{PLAYERMAC} playlist index ?\n" );
  819. IOWrite( $hash, "$hash->{PLAYERMAC} time ?\n" );
  820. # CD 0002 Coverart anfordern, todo: Zeit variabel
  821. $hash->{helper}{CoverOk}=0; # CD 0026 added # CD 0027 changed
  822. # CD 0025 bei lokalen Playlisten schneller abfragen
  823. if( $hash->{ISREMOTESTREAM} eq "0" ) {
  824. InternalTimer( gettimeofday() + 3,
  825. "SB_PLAYER_tcb_QueryCoverArt",
  826. "QueryCoverArt:$name",
  827. 0 );
  828. } else {
  829. InternalTimer( gettimeofday() + 10,
  830. "SB_PLAYER_tcb_QueryCoverArt", # CD 0014 Name geändert
  831. "QueryCoverArt:$name", # CD 0014 Name geändert
  832. 0 );
  833. }
  834. # CD 0002 zu früh, CoverArt ist noch nicht verfügbar
  835. # SB_PLAYER_CoverArt( $hash );
  836. # CD 0000 start - sync players in same group
  837. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  838. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) { # CD 0018 none hinzugefügt
  839. my @pl=split(",",$hash->{SYNCGROUP});
  840. foreach (@pl) {
  841. #Log 0,"SB_Player to sync: $_";
  842. IOWrite( $hash, "$_ artist ?\n" );
  843. IOWrite( $hash, "$_ album ?\n" );
  844. IOWrite( $hash, "$_ title ?\n" );
  845. # CD 0010
  846. IOWrite( $hash, "$_ playlist name ?\n" );
  847. # CD 0014
  848. IOWrite( $hash, "$_ duration ?\n" );
  849. IOWrite( $hash, "$_ playlist index ?\n" );
  850. }
  851. }
  852. }
  853. # CD 0000 end
  854. # CD 0014 start
  855. if(defined($hash->{helper}{recallPause})) {
  856. IOWrite( $hash, "$hash->{PLAYERMAC} pause 1\n" );
  857. RemoveInternalTimer( "recallPause:$name");
  858. InternalTimer( gettimeofday() + 0.5,
  859. "SB_PLAYER_tcb_DeleteRecallPause",
  860. "recallPause:$name",
  861. 0 );
  862. }
  863. # CD 0014 end
  864. # the id is in the last return. ID not reported for radio stations
  865. # so this will go wrong for e.g. Bayern 3
  866. # if( $args[ $#args ] =~ /(^[0-9]{1,3})/g ) {
  867. # readingsBulkUpdate( $hash, "currentMedia", $1 );
  868. # }
  869. } elsif( $args[ 0 ] eq "cant_open" ) {
  870. #TODO: needs to be handled
  871. # CD 0033 TTS abbrechen bei Fehler
  872. if($hash->{helper}{ttsstate}==TTS_WAITFORPLAY) {
  873. SB_PLAYER_TTSStopped($hash);
  874. }
  875. } elsif( $args[ 0 ] eq "open" ) {
  876. readingsBulkUpdate( $hash, "currentMedia", "$args[1]" );
  877. SB_PLAYER_Amplifier( $hash );
  878. SB_PLAYER_GetStatus( $hash ); # CD 0014
  879. # $args[ 2 ] =~ /^(file:)(.*)/g;
  880. # if( defined( $2 ) ) {
  881. #readingsBulkUpdate( $hash, "currentMedia", $2 );
  882. # }
  883. } elsif( $args[ 0 ] eq "repeat" ) {
  884. if( $args[ 1 ] eq "0" ) {
  885. readingsBulkUpdate( $hash, "repeat", "off" );
  886. } elsif( $args[ 1 ] eq "1") {
  887. readingsBulkUpdate( $hash, "repeat", "one" );
  888. } elsif( $args[ 1 ] eq "2") {
  889. readingsBulkUpdate( $hash, "repeat", "all" );
  890. } else {
  891. readingsBulkUpdate( $hash, "repeat", "?" );
  892. }
  893. # CD 0039 Änderung am Master, gesyncte Player aktualisieren
  894. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  895. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  896. my @pl=split(",",$hash->{SYNCGROUP});
  897. foreach (@pl) {
  898. IOWrite( $hash, "$_ playlist repeat ?\n" );
  899. }
  900. }
  901. }
  902. # CD 0039 end
  903. } elsif( $args[ 0 ] eq "shuffle" ) {
  904. if( $args[ 1 ] eq "0" ) {
  905. readingsBulkUpdate( $hash, "shuffle", "off" );
  906. } elsif( $args[ 1 ] eq "1") {
  907. readingsBulkUpdate( $hash, "shuffle", "song" );
  908. } elsif( $args[ 1 ] eq "2") {
  909. readingsBulkUpdate( $hash, "shuffle", "album" );
  910. } else {
  911. readingsBulkUpdate( $hash, "shuffle", "?" );
  912. }
  913. # CD 0039 Änderung am Master, gesyncte Player aktualisieren
  914. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  915. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  916. my @pl=split(",",$hash->{SYNCGROUP});
  917. foreach (@pl) {
  918. IOWrite( $hash, "$_ playlist shuffle ?\n" );
  919. }
  920. }
  921. }
  922. # CD 0039 end
  923. SB_PLAYER_GetStatus( $hash ); # CD 0014
  924. } elsif( $args[ 0 ] eq "name" ) {
  925. # CD 0014 start
  926. $queryMode=0;
  927. if(!defined($args[ 1 ])) {
  928. readingsBulkUpdate( $hash, "currentPlaylistName","-");
  929. readingsBulkUpdate( $hash, "playlists","-");
  930. #$hash->{FAVSELECT} = '-'; # CD 0021 deaktiviert
  931. #readingsBulkUpdate( $hash, "$hash->{FAVSET}", '-' ); # CD 0021 deaktiviert
  932. }
  933. # CD 0014 end
  934. if(defined($args[ 1 ]) && ($args[ 1 ] ne '?')) { # CD 0009 check empty name - 0011 ignore '?'
  935. shift( @args );
  936. readingsBulkUpdate( $hash, "currentPlaylistName",
  937. join( " ", @args ) );
  938. my $pn=SB_SERVER_FavoritesName2UID(join( " ", @args )); # CD 0021 verschoben, decode hinzugefügt # CD 0023 decode entfernt
  939. # CD 0008 update playlists reading
  940. readingsBulkUpdate( $hash, "playlists", $pn); # CD 0021 $pn verwenden wegen Dropdown
  941. # join( "_", @args ) ); # CD 0021 deaktiviert
  942. # CD 0007 start - check if playlist == fav, 0014 removed debug info
  943. if( defined($hash->{helper}{SB_PLAYER_Favs}{$pn}) && defined($hash->{helper}{SB_PLAYER_Favs}{$pn}{ID})) { # CD 0011 check if defined($hash->{helper}{SB_PLAYER_Favs}{$pn})
  944. $hash->{FAVSELECT} = $pn;
  945. readingsBulkUpdate( $hash, "$hash->{FAVSET}", "$pn" );
  946. } else {
  947. $hash->{FAVSELECT} = '-'; # CD 0014
  948. readingsBulkUpdate( $hash, "$hash->{FAVSET}", '-' ); # CD 0014
  949. }
  950. # CD 0007 end
  951. }
  952. # CD 0009 start
  953. # CD 0021 start, update favorites if url matches
  954. } elsif( $args[ 0 ] eq "play" ) {
  955. if(defined($args[ 1 ])) {
  956. $args[ 1 ]=~s/\\/\//g;
  957. $hash->{FAVSELECT}="-";
  958. foreach my $e ( keys %{$hash->{helper}{SB_PLAYER_Favs}} ) {
  959. if($args[ 1 ] eq $hash->{helper}{SB_PLAYER_Favs}{$e}{URL}) {
  960. $hash->{FAVSELECT} = $e;
  961. last;
  962. }
  963. }
  964. readingsBulkUpdate( $hash, "$hash->{FAVSET}", "$hash->{FAVSELECT}" );
  965. # CD 0022 send to synced players # CD 0023 fixed
  966. if( $hash->{SYNCED} eq "yes") {
  967. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  968. my @pl=split(",",$hash->{SYNCGROUP}.",".$hash->{SYNCMASTER});
  969. foreach (@pl) {
  970. if ($hash->{PLAYERMAC} ne $_) {
  971. IOWrite( $hash, "$_ fhemrelay favorites $hash->{FAVSELECT}\n" );
  972. }
  973. }
  974. }
  975. }
  976. }
  977. # CD 0021 end
  978. } elsif( $args[ 0 ] eq "clear" ) {
  979. readingsBulkUpdate( $hash, "currentPlaylistName", "none" );
  980. readingsBulkUpdate( $hash, "playlists", "none" );
  981. # CD 0009 end
  982. SB_PLAYER_GetStatus( $hash ); # CD 0014
  983. } elsif( $args[ 0 ] eq "url" ) {
  984. shift( @args );
  985. readingsBulkUpdate( $hash, "currentPlaylistUrl",
  986. join( " ", @args ) );
  987. } elsif( $args[ 0 ] eq "stop" ) {
  988. readingsBulkUpdate( $hash, "playStatus", "stopped" ); # CD 0012 'power off' durch 'playStatus stopped' ersetzt
  989. SB_PLAYER_Amplifier( $hash );
  990. # CD 0014 start
  991. } elsif( $args[ 0 ] eq "index" ) {
  992. readingsBulkUpdate( $hash, "playlistCurrentTrack", $args[ 1 ] eq '?' ? 0 : $args[ 1 ]+1 ); # Heppel 0056
  993. $queryMode=0;
  994. } elsif( $args[ 0 ] eq "addtracks" ) {
  995. $queryMode=0;
  996. SB_PLAYER_GetStatus( $hash );
  997. } elsif( $args[ 0 ] eq "delete" ) {
  998. $queryMode=0;
  999. #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ); # CD 0016 get available elements for alarms # CD 0026 deaktiviert
  1000. SB_PLAYER_GetStatus( $hash );
  1001. } elsif( $args[ 0 ] eq "load_done" ) {
  1002. if($hash->{helper}{ttsstate}==TTS_PLAYING) {
  1003. #IOWrite( $hash, "$hash->{PLAYERMAC} playlist index +0\n");
  1004. #IOWrite( $hash, "$hash->{PLAYERMAC} play\n" );
  1005. }
  1006. if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST) {
  1007. # CD 0030 start
  1008. if(SB_PLAYER_GetTTSDelay($hash)>0) {
  1009. RemoveInternalTimer( "TTSDelay:$name");
  1010. InternalTimer( gettimeofday() + SB_PLAYER_GetTTSDelay($hash),
  1011. "SB_PLAYER_tcb_TTSDelay",
  1012. "TTSDelay:$name",
  1013. 0 );
  1014. SB_PLAYER_SetTTSState($hash,TTS_DELAY,1,0);
  1015. } else {
  1016. # CD 0030 end
  1017. SB_PLAYER_SetTTSState($hash,TTS_WAITFORPLAY,1,0);
  1018. IOWrite( $hash, "$hash->{PLAYERMAC} play\n" );
  1019. # CD 0038 Timeout hinzugefügt
  1020. RemoveInternalTimer( "TimeoutTTSWaitForPlay:$name");
  1021. InternalTimer( gettimeofday() + 10.00,
  1022. "SB_PLAYER_tcb_TimeoutTTSWaitForPlay",
  1023. "TimeoutTTSWaitForPlay:$name",
  1024. 0 );
  1025. # CD 0038 end
  1026. }
  1027. }
  1028. # CD 0029 start
  1029. if(defined($hash->{helper}{ttsOptions}{logloaddone})) {
  1030. Log3( $hash, 0, "SB_PLAYER_Parse: $name: load_done");
  1031. delete($hash->{helper}{ttsOptions}{logloaddone});
  1032. }
  1033. # CD 0029 end
  1034. if(defined($hash->{helper}{recallPending})) {
  1035. delete($hash->{helper}{recallPending});
  1036. SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,1);
  1037. IOWrite( $hash, "$hash->{PLAYERMAC} play 300\n" );
  1038. IOWrite( $hash, "$hash->{PLAYERMAC} time $hash->{helper}{recallPendingElapsedTime}\n" ); # CD 0047, Position setzen korrigiert
  1039. delete($hash->{helper}{recallPendingElapsedTime}); # CD 0047
  1040. }
  1041. } elsif( $args[ 0 ] eq "loadtracks" ) {
  1042. if(defined($hash->{helper}{recallPending})) {
  1043. delete($hash->{helper}{recallPending});
  1044. SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,1);
  1045. IOWrite( $hash, "$hash->{PLAYERMAC} play 300\n" );
  1046. IOWrite( $hash, "$hash->{PLAYERMAC} time $hash->{helper}{recallPendingElapsedTime}\n" ); # CD 0047, Position setzen korrigiert
  1047. delete($hash->{helper}{recallPendingElapsedTime}); # CD 0047
  1048. }
  1049. # CD 0014 end
  1050. # CD 0048 start
  1051. } elsif( $args[ 0 ] eq "path" ) {
  1052. delete $hash->{helper}{path} if defined($hash->{helper}{path});
  1053. if(defined($args[ 1 ]) && ($args[ 1 ] eq "0")) {
  1054. $hash->{helper}{path}=$args[ 2 ] if defined($args[ 2 ]);
  1055. }
  1056. # CD 0048 end
  1057. } else {
  1058. }
  1059. # check if this caused going to play, as not send automatically
  1060. if(!defined($hash->{helper}{lastModeQuery})||($hash->{helper}{lastModeQuery} < gettimeofday()-0.05)) { # CD 0014 überflüssige Abfragen begrenzen
  1061. IOWrite( $hash, "$hash->{PLAYERMAC} mode ?\n" ) if(!(defined($hash->{helper}{recallPending})||defined($hash->{helper}{recallPause})||($queryMode==0))); # CD 0014 if(... hinzugefügt
  1062. $hash->{helper}{lastModeQuery} = gettimeofday(); # CD 0014
  1063. } # CD 0014
  1064. } elsif( $cmd eq "playlistcontrol" ) {
  1065. #playlistcontrol cmd:load artist_id:22 count:4
  1066. } elsif( $cmd eq "connected" ) {
  1067. readingsBulkUpdate( $hash, "connected", $args[ 0 ] );
  1068. readingsBulkUpdate( $hash, "presence", "present" );
  1069. } elsif( $cmd eq "name" ) {
  1070. $hash->{PLAYERNAME} = join( " ", @args );
  1071. } elsif( $cmd eq "title" ) {
  1072. readingsBulkUpdate( $hash, "currentTitle", join( " ", @args ) );
  1073. } elsif( $cmd eq "artist" ) {
  1074. readingsBulkUpdate( $hash, "currentArtist", join( " ", @args ) );
  1075. } elsif( $cmd eq "album" ) {
  1076. readingsBulkUpdate( $hash, "currentAlbum", join( " ", @args ) );
  1077. } elsif( $cmd eq "player" ) {
  1078. if( $args[ 0 ] eq "model" ) {
  1079. $hash->{MODEL} = $args[ 1 ];
  1080. } elsif( $args[ 0 ] eq "canpoweroff" ) {
  1081. $hash->{CANPOWEROFF} = $args[ 1 ];
  1082. } elsif( $args[ 0 ] eq "ip" ) {
  1083. $hash->{PLAYERIP} = "$args[ 1 ]";
  1084. if( defined( $args[ 2 ] ) ) {
  1085. $hash->{PLAYERIP} .= ":$args[ 2 ]";
  1086. }
  1087. } else {
  1088. }
  1089. } elsif( $cmd eq "power" ) {
  1090. if( !( @args ) ) {
  1091. # no arguments were send with the Power command
  1092. # potentially this is a power toggle : should only happen
  1093. # when called with SB CLI
  1094. } elsif( $args[ 0 ] eq "1" ) {
  1095. readingsBulkUpdate( $hash, "state", "on" );
  1096. readingsBulkUpdate( $hash, "power", "on" );
  1097. SB_PLAYER_Amplifier( $hash );
  1098. } elsif( $args[ 0 ] eq "0" ) {
  1099. #readingsBulkUpdate( $hash, "presence", "absent" ); # CD 0013 deaktiviert, power sagt nichts über presence
  1100. readingsBulkUpdate( $hash, "state", "off" );
  1101. readingsBulkUpdate( $hash, "power", "off" );
  1102. SB_PLAYER_Amplifier( $hash );
  1103. } else {
  1104. # should be "?" normally
  1105. }
  1106. } elsif( $cmd eq "displaytype" ) {
  1107. $hash->{DISPLAYTYPE} = $args[ 0 ];
  1108. } elsif( $cmd eq "signalstrength" ) {
  1109. if( $args[ 0 ] eq "0" ) {
  1110. readingsBulkUpdate( $hash, "signalstrength", "wired" );
  1111. } else {
  1112. readingsBulkUpdate( $hash, "signalstrength", "$args[ 0 ]" );
  1113. }
  1114. } elsif( $cmd eq "alarm" ) {
  1115. if( $args[ 0 ] eq "sound" ) {
  1116. # fired when an alarm goes off
  1117. DoTrigger($name,"alarmSound ".SB_PLAYER_FindAlarmId($hash, $args[ 1 ])) if (SB_PLAYER_check_eocr($hash,'alarmSound')); # CD 0046 # CD 0054 SB_PLAYER_check_eocr hinzugefügt
  1118. } elsif( $args[ 0 ] eq "end" ) {
  1119. # fired when an alarm ends
  1120. DoTrigger($name,"alarmEnd ".SB_PLAYER_FindAlarmId($hash, $args[ 1 ])) if (SB_PLAYER_check_eocr($hash,'alarmEnd')); # CD 0046 # CD 0054 SB_PLAYER_check_eocr hinzugefügt
  1121. } elsif( $args[ 0 ] eq "snooze" ) {
  1122. # fired when an alarm is snoozed by the user
  1123. DoTrigger($name,"alarmSnooze ".SB_PLAYER_FindAlarmId($hash, $args[ 1 ])) if (SB_PLAYER_check_eocr($hash,'alarmSnooze')); # CD 0046 # CD 0054 SB_PLAYER_check_eocr hinzugefügt
  1124. } elsif( $args[ 0 ] eq "snooze_end" ) {
  1125. # fired when an alarm comes back from snooze
  1126. DoTrigger($name,"alarmSnoozeEnd ".SB_PLAYER_FindAlarmId($hash, $args[ 1 ])) if (SB_PLAYER_check_eocr($hash,'alarmSnoozeEnd')); # CD 0046 # CD 0054 SB_PLAYER_check_eocr hinzugefügt
  1127. } elsif( $args[ 0 ] eq "add" ) {
  1128. # fired when an alarm has been added.
  1129. # this setup goes wrong, when an alarm is defined manually
  1130. # the last entry in the array shall contain th id
  1131. my $idstr = $args[ $#args ];
  1132. if( $idstr =~ /^(id:)([0-9a-zA-Z\.]+)/g ) {
  1133. #readingsBulkUpdate( $hash, "alarmid$hash->{LASTALARM}", $2 ); # CD 0015 deaktiviert
  1134. } else {
  1135. }
  1136. #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ) if (!defined($hash->{helper}{alarmPlaylists})); # CD 0015 get available elements for alarms CD 0016 nur wenn nicht vorhanden abfragen # CD 0026 wird über Server verteilt
  1137. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 update alarm list
  1138. } elsif( $args[ 0 ] eq "_cmd" ) {
  1139. #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ); # CD 0015 get available elements for alarms CD 0016 deaktiviert, nicht nötig
  1140. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  1141. } elsif( $args[ 0 ] eq "update" ) {
  1142. #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ) if (!defined($hash->{helper}{alarmPlaylists})); # CD 0015 get available elements for alarms CD 0016 nur wenn nicht vorhanden abfragen # CD 0026 wird über Server verteilt
  1143. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  1144. } elsif( $args[ 0 ] eq "delete" ) {
  1145. if(!defined($hash->{helper}{deleteAllAlarms})) { # CD 0015 do not query while deleting all alarms
  1146. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  1147. }
  1148. # CD 0015 start
  1149. # verfügbare Elemente für Alarme, zwischenspeichern für Anzeige
  1150. # CD 0026 deaktiviert, kommt über Broadcast vom Server
  1151. #} elsif( $args[ 0 ] eq "playlists" ) {
  1152. # delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists}));
  1153. # my @r=split("category:",join(" ",@args));
  1154. # foreach my $a (@r){
  1155. # my $i1=index($a," title:");
  1156. # my $i2=index($a," url:");
  1157. # my $i3=index($a," singleton:");
  1158. # if (($i1!=-1)&&($i2!=-1)&&($i3!=-1)) {
  1159. # my $url=substr($a,$i2+5,$i3-$i2-5);
  1160. # $url=substr($a,$i1+7,$i2-$i1-7) if ($url eq "");
  1161. # my $pn=SB_SERVER_FavoritesName2UID(decode('utf-8',$url)); # CD 0021 decode hinzugefügt
  1162. # $hash->{helper}{alarmPlaylists}{$pn}{category}=substr($a,0,$i1);
  1163. # $hash->{helper}{alarmPlaylists}{$pn}{title}=substr($a,$i1+7,$i2-$i1-7);
  1164. # $hash->{helper}{alarmPlaylists}{$pn}{url}=$url;
  1165. # }
  1166. # }
  1167. # CD 0015
  1168. } else {
  1169. }
  1170. } elsif( $cmd eq "alarms" ) {
  1171. delete($hash->{helper}{deleteAllAlarms}) if(defined($hash->{helper}{deleteAllAlarms})); # CD 0015
  1172. SB_PLAYER_ParseAlarms( $hash, @args );
  1173. } elsif( $cmd eq "showbriefly" ) {
  1174. # to be ignored, we get two hashes
  1175. } elsif( ($cmd eq "unknownir" ) || ($cmd eq "ir" ) ) {
  1176. readingsBulkUpdate( $hash, "lastir", $args[ 0 ] );
  1177. } elsif( $cmd eq "status" ) {
  1178. SB_PLAYER_ParsePlayerStatus( $hash, \@args );
  1179. } elsif( $cmd eq "client" ) {
  1180. if( $args[ 0 ] eq "new" ) {
  1181. # not to be handled here, should lead to a new FHEM Player
  1182. } elsif( $args[ 0 ] eq "disconnect" ) {
  1183. readingsBulkUpdate( $hash, "presence", "absent" );
  1184. readingsBulkUpdate( $hash, "state", "off" );
  1185. readingsBulkUpdate( $hash, "power", "off" );
  1186. SB_PLAYER_Amplifier( $hash );
  1187. # CD 0031 wenn Player während TTS verschwindet Zustand zurücksetzen
  1188. if(($hash->{helper}{ttsstate}>TTS_IDLE)&&($hash->{helper}{ttsstate}<TTS_RESTORE)) {
  1189. $hash->{helper}{savedPlayerState}{power}="off" if(defined($hash->{helper}{savedPlayerState}));
  1190. SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0);
  1191. RemoveInternalTimer( "TTSRestore:$name");
  1192. InternalTimer( gettimeofday() + 0.01,
  1193. "SB_PLAYER_tcb_TTSRestore",
  1194. "TTSRestore:$name",
  1195. 0 );
  1196. }
  1197. # CD 0031 end
  1198. } elsif( $args[ 0 ] eq "reconnect" ) {
  1199. IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ); # CD 0030 u added to tags
  1200. } else {
  1201. }
  1202. } elsif( $cmd eq "prefset" ) {
  1203. if( $args[ 0 ] eq "server" ) {
  1204. if( $args[ 1 ] eq "currentSong" ) {
  1205. # readingsBulkUpdate( $hash, "currentMedia", $args[ 2 ] ); # CD 0014 deaktiviert
  1206. } elsif( $args[ 1 ] eq "volume" ) {
  1207. SB_PLAYER_UpdateVolumeReadings( $hash, $args[ 2 ], true );
  1208. # CD 0000 start - handle 'prefset power' message for synced players
  1209. } elsif( $args[ 1 ] eq "power" ) {
  1210. if( $args[ 2 ] eq "1" ) {
  1211. #Log 0,"$name power on";
  1212. readingsBulkUpdate( $hash, "state", "on" );
  1213. readingsBulkUpdate( $hash, "power", "on" );
  1214. SB_PLAYER_Amplifier( $hash );
  1215. # CD 0038 start
  1216. if($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON) {
  1217. # CD 0042 readingsBulkUpdate abwarten
  1218. RemoveInternalTimer( "TTSStartAfterPowerOn:$name");
  1219. InternalTimer( gettimeofday() + 0.01,
  1220. "SB_PLAYER_tcb_TTSStartAfterPowerOn",
  1221. "TTSStartAfterPowerOn:$name",
  1222. 0 );
  1223. }
  1224. # CD 0038 end
  1225. # CD 0030 send play only after power is on
  1226. if(defined($hash->{helper}{playAfterPowerOn})) {
  1227. IOWrite( $hash, "$hash->{PLAYERMAC} play ".$hash->{helper}{playAfterPowerOn}."\n" );
  1228. delete($hash->{helper}{playAfterPowerOn});
  1229. }
  1230. # CD 0030 end
  1231. } elsif( $args[ 2 ] eq "0" ) {
  1232. #Log 0,"$name power off";
  1233. #readingsBulkUpdate( $hash, "presence", "absent" ); # CD 0013 deaktiviert, power sagt nichts über presence
  1234. readingsBulkUpdate( $hash, "state", "off" );
  1235. readingsBulkUpdate( $hash, "power", "off" );
  1236. SB_PLAYER_Amplifier( $hash );
  1237. delete($hash->{helper}{playAfterPowerOn}) if(defined($hash->{helper}{playAfterPowerOn})); # CD 0030
  1238. # CD 0031 wenn Player während TTS ausgeschaltet wird nicht wieder einschalten
  1239. if(($hash->{helper}{ttsstate}>TTS_IDLE)&&($hash->{helper}{ttsstate}<TTS_RESTORE)) {
  1240. $hash->{helper}{savedPlayerState}{power}="off" if(defined($hash->{helper}{savedPlayerState}));
  1241. SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0);
  1242. RemoveInternalTimer( "TTSRestore:$name");
  1243. InternalTimer( gettimeofday() + 0.01,
  1244. "SB_PLAYER_tcb_TTSRestore",
  1245. "TTSRestore:$name",
  1246. 0 );
  1247. }
  1248. # CD 0031 end
  1249. }
  1250. # CD 0000 end
  1251. # CD 0010 start prefset server mute
  1252. } elsif( $args[ 1 ] eq "mute" ) {
  1253. SB_PLAYER_SetSyncedVolume($hash, -1) if ($args[ 2 ] == 1);
  1254. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ?\n" ) if ($args[ 2 ] == 0);
  1255. # CD 0010 end
  1256. # CD 0016 start
  1257. } elsif( $args[ 1 ] eq "alarmTimeoutSeconds" ) {
  1258. readingsBulkUpdate( $hash, "alarmsTimeout", $args[ 2 ]/60 );
  1259. } elsif( $args[ 1 ] eq "alarmSnoozeSeconds" ) {
  1260. readingsBulkUpdate( $hash, "alarmsSnooze", $args[ 2 ]/60 );
  1261. } elsif( $args[ 1 ] eq "alarmDefaultVolume" ) {
  1262. readingsBulkUpdate( $hash, "alarmsDefaultVolume", $args[ 2 ] ); # CD 0052 fixed
  1263. } elsif( $args[ 1 ] eq "alarmfadeseconds" ) {
  1264. if($args[ 2 ] eq "1") {
  1265. readingsBulkUpdate( $hash, "alarmsFadeIn", "on" );
  1266. } else {
  1267. readingsBulkUpdate( $hash, "alarmsFadeIn", "off" );
  1268. }
  1269. # CD 0016 end
  1270. # CD 0018 start
  1271. } elsif( $args[ 1 ] eq "syncgroupid" ) {
  1272. IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ); # CD 0030 u added to tags
  1273. # CD 0018 end
  1274. # CD 0039 für gesyncte Player bei Änderung am Slave erhält der Master eine prefset Meldung, an alle Slaves weitergeben
  1275. } elsif( $args[ 1 ] eq "repeat" ) {
  1276. if( $args[ 2 ] eq "0" ) {
  1277. readingsBulkUpdate( $hash, "repeat", "off" );
  1278. } elsif( $args[ 2 ] eq "1") {
  1279. readingsBulkUpdate( $hash, "repeat", "one" );
  1280. } elsif( $args[ 2 ] eq "2") {
  1281. readingsBulkUpdate( $hash, "repeat", "all" );
  1282. } else {
  1283. readingsBulkUpdate( $hash, "repeat", "?" );
  1284. }
  1285. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  1286. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  1287. my @pl=split(",",$hash->{SYNCGROUP});
  1288. if(@pl>1) {
  1289. foreach (@pl) {
  1290. IOWrite( $hash, "$_ playlist repeat ?\n" );
  1291. }
  1292. }
  1293. }
  1294. }
  1295. } elsif( $args[ 1 ] eq "shuffle" ) {
  1296. if( $args[ 2 ] eq "0" ) {
  1297. readingsBulkUpdate( $hash, "shuffle", "off" );
  1298. } elsif( $args[ 2 ] eq "1") {
  1299. readingsBulkUpdate( $hash, "shuffle", "song" );
  1300. } elsif( $args[ 2 ] eq "2") {
  1301. readingsBulkUpdate( $hash, "shuffle", "album" );
  1302. } else {
  1303. readingsBulkUpdate( $hash, "shuffle", "?" );
  1304. }
  1305. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  1306. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  1307. my @pl=split(",",$hash->{SYNCGROUP});
  1308. if(@pl>1) {
  1309. foreach (@pl) {
  1310. IOWrite( $hash, "$_ playlist shuffle ?\n" );
  1311. }
  1312. }
  1313. }
  1314. }
  1315. SB_PLAYER_GetStatus( $hash );
  1316. # CD 0039 end
  1317. }
  1318. } else {
  1319. readingsBulkUpdate( $hash, "lastunknowncmd",
  1320. $cmd . " " . join( " ", @args ) );
  1321. }
  1322. # CD 0007 start
  1323. } elsif( $cmd eq "playerpref" ) {
  1324. if( $args[ 0 ] eq "syncVolume" ) {
  1325. if (defined($args[1])) {
  1326. $hash->{SYNCVOLUME}=$args[1];
  1327. my $sva=AttrVal($hash->{NAME}, "syncVolume", undef);
  1328. # force attribute
  1329. if (defined($sva)) {
  1330. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume 0\n" ) if(($sva ne "1") && ($args[1] ne "0"));
  1331. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume 1\n" ) if(($sva eq "1") && ($args[1] ne "1"));
  1332. }
  1333. }
  1334. }
  1335. # CD 0007 end
  1336. # CD 0016 start, von MM übernommen, Namen Readings geändert
  1337. elsif( $args[ 0 ] eq "alarmsEnabled" ) {
  1338. if (defined($args[1])) {
  1339. if( $args[1] eq "1" ) {
  1340. readingsBulkUpdate( $hash, "alarmsEnabled", "on" ); # CD 0016 Internal durch Reading ersetzt # CD 0017 'yes' durch 'on' ersetzt
  1341. } else {
  1342. readingsBulkUpdate( $hash, "alarmsEnabled", "off" ); # CD 0016 Internal durch Reading ersetzt # CD 0017 'no' durch 'off' ersetzt
  1343. }
  1344. }
  1345. }
  1346. elsif( $args[ 0 ] eq "alarmDefaultVolume" ) {
  1347. if (defined($args[1]) && ($args[1] ne "?")) { # CD 0016 Rückmeldung auf Anfrage ignorieren
  1348. #$hash->{ALARMSVOLUME} = $args[1]; # CD 0016 nicht benötigt
  1349. readingsBulkUpdate( $hash, "alarmsDefaultVolume", $args[ 1 ] );
  1350. }
  1351. }
  1352. elsif( $args[ 0 ] eq "alarmTimeoutSeconds" ) {
  1353. if (defined($args[1]) && ($args[1] ne "?")) { # CD 0016 Rückmeldung auf Anfrage ignorieren
  1354. #$hash->{ALARMSTIMEOUT} = $args[1]/60 . " min"; # CD 0016 nicht benötigt
  1355. readingsBulkUpdate( $hash, "alarmsTimeout", $args[ 1 ]/60 );
  1356. }
  1357. }
  1358. elsif( $args[ 0 ] eq "alarmSnoozeSeconds" ) {
  1359. if (defined($args[1]) && ($args[1] ne "?")) { # CD 0016 Rückmeldung auf Anfrage ignorieren
  1360. #$hash->{ALARMSSNOOZE} = $args[1]/60 . " min"; # CD 0016 nicht benötigt
  1361. readingsBulkUpdate( $hash, "alarmsSnooze", $args[ 1 ]/60 );
  1362. }
  1363. }
  1364. # CD 0016 end
  1365. # CD 0014 start
  1366. } elsif( $cmd eq "duration" ) {
  1367. readingsBulkUpdate( $hash, "duration", $args[ 0 ] );
  1368. } elsif( $cmd eq "time" ) {
  1369. $args[0]=0 unless defined($args[ 0 ]); # CD 0059
  1370. $args[0]=0 if($args[ 0 ] eq '?'); # CD 0050
  1371. $hash->{helper}{elapsedTime}{VAL}=$args[ 0 ];
  1372. $hash->{helper}{elapsedTime}{TS}=gettimeofday();
  1373. readingsBulkUpdate( $hash, "currentTrackPosition",int($args[ 0 ]+0.5)); # CD 0047
  1374. delete($hash->{helper}{saveLocked}) if (($hash->{helper}{ttsstate}==TTS_IDLE) && defined($hash->{helper}{saveLocked}));
  1375. } elsif( $cmd eq "playlist_tracks" ) {
  1376. readingsBulkUpdate( $hash, "playlistTracks", $args[ 0 ] );
  1377. # CD 0014 end
  1378. # CD 0018 sync Meldungen auswerten, alle anderen Player abfragen
  1379. } elsif( $cmd eq "sync" ) {
  1380. foreach my $e ( keys %{$hash->{helper}{SB_PLAYER_SyncMasters}} ) {
  1381. IOWrite( $hash, $hash->{helper}{SB_PLAYER_SyncMasters}{$e}{MAC}." status 0 500 tags:Kcu\n" ) if defined($hash->{helper}{SB_PLAYER_SyncMasters}{$e}{MAC}); # CD 0039 u hinzugefügt # CD 0056 if defined hinzugefügt
  1382. }
  1383. IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ); # CD 0056 auch betroffenen Player abfragen
  1384. # CD 0018
  1385. # CD 0052 jivealarm Meldungen ignorieren
  1386. } elsif( $cmd eq "jivealarm" ) {
  1387. # CD 0018
  1388. # CD 0022 fhemrelay ist keine Meldung des LMS sondern eine Info die von einem anderen Player über 98_SB_PLAYER kommt
  1389. } elsif( $cmd eq "fhemrelay" ) {
  1390. if (defined($args[0])) {
  1391. # CD 0022 Favoriten vom Sync-Master übernehmen
  1392. if ($args[0] eq "favorites") {
  1393. if (defined($args[1])) {
  1394. $hash->{FAVSELECT} = $args[1];
  1395. readingsBulkUpdate( $hash, "$hash->{FAVSET}", "$hash->{FAVSELECT}" );
  1396. }
  1397. }
  1398. # CD 0028 tts aktiv
  1399. elsif ($args[0] eq "ttsactive") {
  1400. $hash->{helper}{ttsMaster}=$args[1];
  1401. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsactive ".$hash->{helper}{ttsMaster} );
  1402. SB_PLAYER_SetTTSState($hash,TTS_SYNCGROUPACTIVE,1,0);
  1403. # CD 0031 Lautstärke setzen
  1404. if(!defined($hash->{SYNCVOLUME}) || ($hash->{SYNCVOLUME}==0)) {
  1405. if(defined($hash->{helper}{ttsVolume})) {
  1406. $hash->{helper}{ttsRestoreVolumeAfterStop}=ReadingsVal($name,"volumeStraight","?");
  1407. my $vol=$hash->{helper}{ttsVolume};
  1408. $vol=AttrVal( $name, "volumeLimit", 100 ) if(( $hash->{helper}{ttsVolume} > AttrVal( $name, "volumeLimit", 100 ) )&&!defined($hash->{helper}{ttsOptions}{ignorevolumelimit}));
  1409. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".$vol."\n" );
  1410. }
  1411. }
  1412. # CD 0031 end
  1413. }
  1414. elsif ($args[0] eq "ttsstopped") {
  1415. # CD 0056 Meldung ignorieren wenn Player Syncmaster ist
  1416. if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) {
  1417. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsstopped, ignoring" );
  1418. } else {
  1419. # CD 0056 end
  1420. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsstopped" );
  1421. if($hash->{helper}{ttsstate}==TTS_PLAYING) {
  1422. # CD 0034 delay ttsstopped
  1423. RemoveInternalTimer( "TTSStopped:$name");
  1424. InternalTimer( gettimeofday() + 0.01,
  1425. "SB_PLAYER_tcb_TTSStopped",
  1426. "TTSRestore:$name",
  1427. 0 );
  1428. }
  1429. }
  1430. }
  1431. elsif ($args[0] eq "ttsplaying") {
  1432. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsplaying" );
  1433. if($hash->{helper}{ttsstate}==TTS_WAITFORPLAY) {
  1434. SB_PLAYER_SetTTSState($hash,TTS_PLAYING,1,0);
  1435. }
  1436. }
  1437. elsif ($args[0] eq "ttsidle") {
  1438. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsidle" );
  1439. SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,0);
  1440. # CD 0030 start
  1441. if(defined($hash->{helper}{ttspoweroffafterstop})) {
  1442. IOWrite( $hash, "$hash->{PLAYERMAC} power 0\n" );
  1443. delete($hash->{helper}{ttspoweroffafterstop});
  1444. }
  1445. # CD 0030 end
  1446. # CD 0031 Lautstärke zurücksetzen
  1447. if(defined($hash->{helper}{ttsRestoreVolumeAfterStop})) {
  1448. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".($hash->{helper}{ttsRestoreVolumeAfterStop})."\n" );
  1449. delete($hash->{helper}{ttsRestoreVolumeAfterStop});
  1450. }
  1451. # CD 0031 end
  1452. }
  1453. elsif ($args[0] eq "ttsadd") {
  1454. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsadd $args[1]" );
  1455. push(@{$hash->{helper}{ttsqueue}},$args[1]);
  1456. }
  1457. # CD 0030 start
  1458. elsif ($args[0] eq "ttsforcegroupon") {
  1459. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsforcegroupon" );
  1460. if( $hash->{CANPOWEROFF} ne "0" ) {
  1461. IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" );
  1462. $hash->{helper}{ttspoweroffafterstop}=1;
  1463. }
  1464. }
  1465. # CD 0030 end
  1466. }
  1467. # CD 0022 end
  1468. } elsif( $cmd eq "NONE" ) {
  1469. # we shall never end up here, as cmd=NONE is used by the server for
  1470. # autocreate
  1471. } else {
  1472. # unknown command, we push it to the last command thingy
  1473. readingsBulkUpdate( $hash, "lastunknowncmd",
  1474. $cmd . " " . join( " ", @args ) );
  1475. }
  1476. # and signal the end of the readings update
  1477. if( AttrVal( $name, "donotnotify", "false" ) eq "true" ) {
  1478. readingsEndUpdate( $hash, 0 );
  1479. } else {
  1480. readingsEndUpdate( $hash, 1 );
  1481. }
  1482. Log3( $hash, 5, "SB_PLAYER_Parse: $name: leaving" );
  1483. return( $name );
  1484. }
  1485. # CD 0054
  1486. # ----------------------------------------------------------------------------
  1487. # check if value is in 'event-on-change-reading' list
  1488. # ----------------------------------------------------------------------------
  1489. sub SB_PLAYER_check_eocr($$) {
  1490. my ($hash,$reading) = @_;
  1491. my $name = $hash->{NAME};
  1492. my $attreocr = $hash->{".attreocr"};
  1493. # determine whether the reading is listed in any of the attributes
  1494. my $eocr = $attreocr &&
  1495. ( my @eocrv = grep { my $l = $_; $l =~ s/:.*//;
  1496. ($reading=~ m/^$l$/) ? $_ : undef} @{$attreocr});
  1497. return !($attreocr) || ($eocr);
  1498. }
  1499. # CD 0030
  1500. # ----------------------------------------------------------------------------
  1501. # delay TTS
  1502. # ----------------------------------------------------------------------------
  1503. sub SB_PLAYER_tcb_TTSDelay( $ ) {
  1504. my($in ) = shift;
  1505. my(undef,$name) = split(':',$in);
  1506. my $hash = $defs{$name};
  1507. SB_PLAYER_SetTTSState($hash,TTS_WAITFORPLAY,0,0);
  1508. IOWrite( $hash, "$hash->{PLAYERMAC} play\n" );
  1509. # CD 0038 Timeout hinzugefügt
  1510. RemoveInternalTimer( "TimeoutTTSWaitForPlay:$name");
  1511. InternalTimer( gettimeofday() + 10.00,
  1512. "SB_PLAYER_tcb_TimeoutTTSWaitForPlay",
  1513. "TimeoutTTSWaitForPlay:$name",
  1514. 0 );
  1515. # CD 0038 end
  1516. }
  1517. # CD 0030
  1518. # CD 0038
  1519. # ----------------------------------------------------------------------------
  1520. # TTS 'wait for play' timeout
  1521. # ----------------------------------------------------------------------------
  1522. sub SB_PLAYER_tcb_TimeoutTTSWaitForPlay( $ ) {
  1523. my($in ) = shift;
  1524. my(undef,$name) = split(':',$in);
  1525. my $hash = $defs{$name};
  1526. if($hash->{helper}{ttsstate}==TTS_WAITFORPLAY) {
  1527. readingsBeginUpdate( $hash );
  1528. SB_PLAYER_TTSStopped($hash);
  1529. if( AttrVal( $name, "donotnotify", "false" ) eq "true" ) {
  1530. readingsEndUpdate( $hash, 0 );
  1531. } else {
  1532. readingsEndUpdate( $hash, 1 );
  1533. }
  1534. }
  1535. }
  1536. # ----------------------------------------------------------------------------
  1537. # TTS 'wait for power on' timeout
  1538. # ----------------------------------------------------------------------------
  1539. sub SB_PLAYER_tcb_TimeoutTTSWaitForPowerOn( $ ) {
  1540. my($in ) = shift;
  1541. my(undef,$name) = split(':',$in);
  1542. my $hash = $defs{$name};
  1543. if($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON) {
  1544. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,0);
  1545. }
  1546. }
  1547. # CD 0038
  1548. # CD 0034
  1549. # ----------------------------------------------------------------------------
  1550. # delay ttsstopped
  1551. # ----------------------------------------------------------------------------
  1552. sub SB_PLAYER_tcb_TTSStopped( $ ) {
  1553. my($in ) = shift;
  1554. my(undef,$name) = split(':',$in);
  1555. my $hash = $defs{$name};
  1556. readingsBeginUpdate( $hash );
  1557. SB_PLAYER_TTSStopped($hash);
  1558. if( AttrVal( $name, "donotnotify", "false" ) eq "true" ) {
  1559. readingsEndUpdate( $hash, 0 );
  1560. } else {
  1561. readingsEndUpdate( $hash, 1 );
  1562. }
  1563. }
  1564. # CD 0034 end
  1565. # CD 0042
  1566. # ----------------------------------------------------------------------------
  1567. # start tts after power on, wait if startup is not complete
  1568. # ----------------------------------------------------------------------------
  1569. sub SB_PLAYER_tcb_TTSStartAfterPowerOn( $ ) {
  1570. my($in ) = shift;
  1571. my(undef,$name) = split(':',$in);
  1572. my $hash = $defs{$name};
  1573. if(($hash->{helper}{playerStatusOK}==1)||($hash->{helper}{playerStatusOKCounter}>10)) {
  1574. SB_PLAYER_PrepareTalk($hash);
  1575. SB_PLAYER_LoadTalk($hash);
  1576. } else {
  1577. InternalTimer( gettimeofday() + 1,
  1578. "SB_PLAYER_tcb_TTSStartAfterPowerOn",
  1579. "TTSStartAfterPowerOn:$name",
  1580. 0 );
  1581. $hash->{helper}{playerStatusOKCounter}++;
  1582. }
  1583. }
  1584. # CD 0042 end
  1585. # ----------------------------------------------------------------------------
  1586. # called when talk is stopped, check if there are queued elements
  1587. # ----------------------------------------------------------------------------
  1588. sub SB_PLAYER_TTSStopped($) {
  1589. # readingsBulkUpdate muss aktiv sein
  1590. my ($hash) = @_;
  1591. my $name = $hash->{NAME};
  1592. if(defined($hash->{helper}{ttsqueue})) {
  1593. SB_PLAYER_SetTTSState($hash,TTS_LOADPLAYLIST,1,0);
  1594. SB_PLAYER_LoadTalk($hash); # CD 0033
  1595. } else {
  1596. SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0);
  1597. RemoveInternalTimer( "TTSRestore:$name");
  1598. InternalTimer( gettimeofday() + 0.01,
  1599. "SB_PLAYER_tcb_TTSRestore",
  1600. "TTSRestore:$name",
  1601. 0 );
  1602. }
  1603. }
  1604. # ----------------------------------------------------------------------------
  1605. # Undefinition of an SB_PLAYER
  1606. # called when undefining (delete) and element
  1607. # ----------------------------------------------------------------------------
  1608. sub SB_PLAYER_Undef( $$$ ) {
  1609. my ($hash, $arg) = @_;
  1610. Log3( $hash, 5, "SB_PLAYER_Undef: called" );
  1611. RemoveInternalTimer( $hash );
  1612. # to be reviewed if that works.
  1613. # check for uc()
  1614. # what is $hash->{DEF}?
  1615. delete $modules{SB_PLAYER}{defptr}{uc($hash->{DEF})};
  1616. return( undef );
  1617. }
  1618. # ----------------------------------------------------------------------------
  1619. # Shutdown function - called before fhem shuts down
  1620. # ----------------------------------------------------------------------------
  1621. sub SB_PLAYER_Shutdown( $$ ) {
  1622. my ($hash, $dev) = @_;
  1623. RemoveInternalTimer( $hash );
  1624. Log3( $hash, 5, "SB_PLAYER_Shutdown: called" );
  1625. return( undef );
  1626. }
  1627. # ----------------------------------------------------------------------------
  1628. # Get of a module
  1629. # called upon get <name> cmd, arg1, arg2, ....
  1630. # ----------------------------------------------------------------------------
  1631. sub SB_PLAYER_Get( $@ ) {
  1632. my ($hash, @a) = @_;
  1633. my $name = $hash->{NAME};
  1634. Log3( $hash, 4, "SB_PLAYER_Get: called with @a" );
  1635. if( @a < 2 ) {
  1636. my $msg = "SB_PLAYER_Get: $name: wrong number of arguments";
  1637. Log3( $hash, 5, $msg );
  1638. return( $msg );
  1639. }
  1640. #my $name = shift( @a );
  1641. shift( @a ); # name
  1642. my $cmd = shift( @a );
  1643. if( $cmd eq "?" ) {
  1644. my $res = "Unknown argument ?, choose one of " .
  1645. "volume " . $hash->{FAVSET} . " savedStates alarmPlaylists"; # CD 0045 alarmPlaylists hinzugefügt
  1646. return( $res );
  1647. } elsif( ( $cmd eq "volume" ) || ( $cmd eq "volumeStraight" ) ) {
  1648. return( scalar( ReadingsVal( "$name", "volumeStraight", 25 ) ) );
  1649. } elsif( $cmd eq $hash->{FAVSET} ) {
  1650. return( "$hash->{FAVSELECT}" );
  1651. # CD 0036 start
  1652. } elsif( $cmd eq 'savedStates' ) {
  1653. my $out="";
  1654. if (defined($hash->{helper}{savedPlayerState})) {
  1655. foreach my $pl ( keys %{$hash->{helper}{savedPlayerState}} ) {
  1656. $out.=$pl."\n" unless ($pl=~/xxTTSxx/);
  1657. }
  1658. }
  1659. return( $out );
  1660. # CD 0036 end
  1661. # CD 0045 start
  1662. } elsif( $cmd eq 'alarmPlaylists' ) {
  1663. my $out="";
  1664. if (defined($hash->{helper}{alarmPlaylists})) {
  1665. foreach my $e ( keys %{$hash->{helper}{alarmPlaylists}} ) {
  1666. $out.=$hash->{helper}{alarmPlaylists}{$e}{title}."\n";
  1667. }
  1668. }
  1669. return( $out );
  1670. # CD 0045 end
  1671. } else {
  1672. my $msg = "SB_PLAYER_Get: $name: unkown argument";
  1673. Log3( $hash, 5, $msg );
  1674. return( $msg );
  1675. }
  1676. return( undef );
  1677. }
  1678. # CD 0030 start
  1679. # ----------------------------------------------------------------------------
  1680. # Calculate delay for TTS
  1681. # ----------------------------------------------------------------------------
  1682. sub SB_PLAYER_GetTTSDelay( $ ) {
  1683. my ( $hash ) = @_;
  1684. my $name = $hash->{NAME};
  1685. # todo synced players
  1686. if(defined($hash->{helper}{ttsDelay})) {
  1687. if(ReadingsVal($name,"power","x") eq "on") {
  1688. return $hash->{helper}{ttsDelay}{PowerIsOn}
  1689. } else {
  1690. return $hash->{helper}{ttsDelay}{PowerIsOff}
  1691. }
  1692. } else {
  1693. return 0;
  1694. }
  1695. }
  1696. # CD 0030 end
  1697. # CD 0033 start
  1698. # ----------------------------------------------------------------------------
  1699. # called after Text2Speech has finished, start talk
  1700. # ----------------------------------------------------------------------------
  1701. sub SB_PLAYER_tcb_StartT2STalk( $ ) {
  1702. my($in ) = shift;
  1703. my(undef,$name) = split(':',$in);
  1704. my $hash = $defs{$name};
  1705. if($hash->{helper}{ttsstate}==TTS_TEXT2SPEECH_ACTIVE) {
  1706. # talk ist nicht aktiv
  1707. SB_PLAYER_PrepareTalk($hash);
  1708. }
  1709. SB_PLAYER_LoadTalk($hash);
  1710. }
  1711. # CD 0033 end
  1712. # ----------------------------------------------------------------------------
  1713. # the Notify function
  1714. # ----------------------------------------------------------------------------
  1715. sub SB_PLAYER_Notify( $$ ) {
  1716. my ( $hash, $dev_hash ) = @_;
  1717. my $name = $hash->{NAME}; # own name / hash
  1718. my $devName = $dev_hash->{NAME}; # Device that created the events
  1719. if ($dev_hash->{NAME} eq "global" && grep (m/^INITIALIZED$|^REREADCFG$/,@{$dev_hash->{CHANGED}})){
  1720. }
  1721. # CD 0036 start
  1722. if( grep(m/^SAVE$|^SHUTDOWN$/, @{$dev_hash->{CHANGED}}) ) { # CD 0043 auch bei SHUTDOWN speichern
  1723. SB_PLAYER_SavePlayerStates($hash) if($SB_PLAYER_hasDataDumper==1);
  1724. }
  1725. # CD 0036 end
  1726. # CD 0033 start
  1727. if(defined($hash->{helper}{text2speech}{name}) && ($hash->{helper}{text2speech}{name} eq $devName)) {
  1728. $hash->{helper}{ttsExtstate}=TTS_IDLE if(!defined($hash->{helper}{ttsExtstate}));
  1729. if(($hash->{helper}{ttsstate}==TTS_TEXT2SPEECH_ACTIVE)||($hash->{helper}{ttsExtstate}==TTS_EXT_TEXT2SPEECH_ACTIVE)) {
  1730. foreach my $line (@{$dev_hash->{CHANGED}}) {
  1731. my @args=split(' ',$line);
  1732. if ($args[0] eq $name) {
  1733. if($args[1] eq "ttsadd") {
  1734. push(@{$hash->{helper}{ttsqueue}},$hash->{helper}{text2speech}{pathPrefix}.$args[2]);
  1735. }
  1736. elsif($args[1] eq 'ttsdone') {
  1737. RemoveInternalTimer( "StartTalk:$name");
  1738. InternalTimer( gettimeofday() + 0.01,
  1739. "SB_PLAYER_tcb_StartT2STalk",
  1740. "StartTalk:$name",
  1741. 0 );
  1742. }
  1743. }
  1744. }
  1745. } elsif (($hash->{helper}{ttsstate}==TTS_TEXT2SPEECH_BUSY)||($hash->{helper}{ttsExtstate}==TTS_EXT_TEXT2SPEECH_BUSY)) {
  1746. # versuchen Text2Speech zu belegen
  1747. if(defined($dev_hash->{helper}{SB_PLAYER}) || (defined($dev_hash->{helper}{Text2Speech}) && @{$dev_hash->{helper}{Text2Speech}} > 0)) {
  1748. # zu spät, weiter warten
  1749. } else {
  1750. $dev_hash->{helper}{SB_PLAYER}=$name;
  1751. if($hash->{helper}{ttsstate}==TTS_TEXT2SPEECH_BUSY) {
  1752. SB_PLAYER_SetTTSState($hash,TTS_TEXT2SPEECH_ACTIVE,0,0);
  1753. } else {
  1754. $hash->{helper}{ttsExtstate}=TTS_EXT_TEXT2SPEECH_ACTIVE;
  1755. }
  1756. fhem("set $devName tts ".($hash->{helper}{text2speech}{text}));
  1757. delete($hash->{helper}{text2speech}{text});
  1758. }
  1759. }
  1760. }
  1761. # CD 0033 end
  1762. }
  1763. # ----------------------------------------------------------------------------
  1764. # Set of a module
  1765. # called upon set <name> cmd, arg1, arg2, ....
  1766. # ----------------------------------------------------------------------------
  1767. sub SB_PLAYER_Set( $@ ) {
  1768. my ( $hash, $name, $cmd, @arg ) = @_;
  1769. #my $name = $hash->{NAME};
  1770. Log3( $hash, 5, "SB_PLAYER_Set: called with $cmd");
  1771. # check if we have received a command
  1772. if( !defined( $cmd ) ) {
  1773. my $msg = "$name: set needs at least one parameter";
  1774. Log3( $hash, 3, $msg );
  1775. return( $msg );
  1776. }
  1777. # now parse the commands
  1778. if( $cmd eq "?" ) {
  1779. # this one should give us a drop down list
  1780. my $res = "Unknown argument ?, choose one of " .
  1781. "on off stop:noArg play:noArg pause:noArg " .
  1782. "save " . # CD 0014 # CD 0036 removed :noArg
  1783. "volume:slider,0,1,100 " .
  1784. "volumeStraight:slider,0,1,100 " .
  1785. "volumeUp:noArg volumeDown:noArg " .
  1786. "mute:noArg repeat:off,one,all show statusRequest:noArg " .
  1787. "shuffle:off,song,album next:noArg prev:noArg playlist sleep " . # CD 0017 song und album hinzugefügt # CD 0052 shuffle on entfernt
  1788. "allalarms:enable,disable,statusRequest,delete,add " . # CD 0015 alarm1 alarm2 entfernt
  1789. "alarmsSnooze:slider,0,1,30 alarmsTimeout:slider,0,5,90 alarmsDefaultVolume:slider,0,1,100 alarmsFadeIn:on,off alarmsEnabled:on,off " . # CD 0016, von MM übernommen, Namen geändert
  1790. "cliraw talk sayText " . # CD 0014 sayText hinzugefügt
  1791. "unsync:noArg " .
  1792. "currentTrackPosition " . # CD 0047 hinzugefügt
  1793. "snooze:noArg " . # CD 0052 hinzugefügt
  1794. "resetTTS:noArg "; # CD 0028 hinzugefügt
  1795. # add the favorites
  1796. $res .= $hash->{FAVSET} . ":-," . $hash->{FAVSTR} . " "; # CD 0014 '-' hinzugefügt
  1797. # add the syncmasters
  1798. $res .= "sync:" . $hash->{SYNCMASTERS} . " ";
  1799. # add the playlists
  1800. $res .= "playlists:-," . $hash->{SERVERPLAYLISTS} . " "; # CD 0014 '-' hinzugefügt
  1801. # add player saved lists # CD 0036
  1802. my $out="";
  1803. if (defined($hash->{helper}{savedPlayerState})) {
  1804. foreach my $pl ( keys %{$hash->{helper}{savedPlayerState}} ) {
  1805. $out.=$pl."," unless ($pl=~/xxTTSxx/);
  1806. }
  1807. $out=~s/,$//;
  1808. }
  1809. $res .= "recall:$out ";
  1810. # CD 0016 start {ALARMSCOUNT} verschieben nach reload
  1811. if (defined($hash->{ALARMSCOUNT})) {
  1812. $hash->{helper}{ALARMSCOUNT}=$hash->{ALARMSCOUNT};
  1813. delete($hash->{ALARMSCOUNT});
  1814. }
  1815. # CD 0016 end
  1816. # CD 0015 - add the alarms
  1817. if (defined($hash->{helper}{ALARMSCOUNT})&&($hash->{helper}{ALARMSCOUNT}>0)) { # CD 0016 ALARMSCOUNT nach {helper} verschoben
  1818. for(my $i=1;$i<=$hash->{helper}{ALARMSCOUNT};$i++) { # CD 0016 ALARMSCOUNT nach {helper} verschoben
  1819. $res .="alarm$i ";
  1820. }
  1821. }
  1822. return( $res );
  1823. }
  1824. # CD 0038 Befehle ignorieren wenn Player nicht vorhanden ist
  1825. # CD 0040 wieder deaktiviert
  1826. # if(ReadingsVal($name,"presence","x") ne "present") {
  1827. # return "$name: player is not available";
  1828. # }
  1829. # CD 0038 end
  1830. my $updateReadingsOnSet=AttrVal($name, "updateReadingsOnSet", false); # CD 0017
  1831. my $donotnotify=AttrVal($name, "donotnotify", "true"); # CD 0017 # CD 0028 added "
  1832. # as we have some other command, we need to turn on the server
  1833. #if( AttrVal( $name, "serverautoon", "true" ) eq "true" ) {
  1834. #SB_PLAYER_ServerTurnOn( $hash );
  1835. #}
  1836. if( ( $cmd eq "Stop" ) || ( $cmd eq "STOP" ) || ( $cmd eq "stop" ) ) {
  1837. IOWrite( $hash, "$hash->{PLAYERMAC} stop\n" );
  1838. } elsif( ( $cmd eq "Play" ) || ( $cmd eq "PLAY" ) || ( $cmd eq "play" ) ) {
  1839. my @secbuf = split(',',AttrVal( $name, "fadeinsecs", '10,10' )); # CD 0050 split hinzugefügt
  1840. $secbuf[1]=$secbuf[0] if (@secbuf==1); # CD 0051
  1841. # CD 0030 wait until power on
  1842. if(ReadingsVal($name,"power","x") eq "on") {
  1843. # CD 0051 2. Wert von fadeinsecs bei pause -> play verwenden
  1844. if (ReadingsVal($name,"playStatus","?") eq 'paused') {
  1845. IOWrite( $hash, "$hash->{PLAYERMAC} play ".$secbuf[1]."\n" );
  1846. } else {
  1847. IOWrite( $hash, "$hash->{PLAYERMAC} play ".$secbuf[0]."\n" );
  1848. }
  1849. } else {
  1850. $hash->{helper}{playAfterPowerOn}=$secbuf[0];
  1851. IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" );
  1852. }
  1853. # CD 0030 end
  1854. } elsif( ( $cmd eq "Pause" ) || ( $cmd eq "PAUSE" ) || ( $cmd eq "pause" ) ) {
  1855. my @secbuf = split(',',AttrVal( $name, "fadeinsecs", '10,10' )); # CD 0050 split hinzugefügt
  1856. $secbuf[1]=$secbuf[0] if (@secbuf==1); # CD 0050
  1857. if( @arg == 1 ) {
  1858. if( $arg[ 0 ] eq "1" ) {
  1859. # pause the player
  1860. IOWrite( $hash, "$hash->{PLAYERMAC} pause 1\n" );
  1861. } else {
  1862. # unpause the player
  1863. IOWrite( $hash, "$hash->{PLAYERMAC} pause 0 ".$secbuf[1]."\n" );
  1864. }
  1865. } else {
  1866. # IOWrite( $hash, "$hash->{PLAYERMAC} pause $secbuf\n" ); # CD 0050 deaktiviert, funktioniert so nicht, wenn $secbuf verwendet wird muss 0 oder 1 davorstehen
  1867. # CD 0050 start
  1868. if (ReadingsVal($name,"playStatus","?") eq 'playing') {
  1869. IOWrite( $hash, "$hash->{PLAYERMAC} pause 1\n" );
  1870. } else {
  1871. IOWrite( $hash, "$hash->{PLAYERMAC} pause 0 ".$secbuf[1]."\n" );
  1872. }
  1873. # CD 0050 end
  1874. }
  1875. } elsif( ( $cmd eq "next" ) || ( $cmd eq "NEXT" ) || ( $cmd eq "Next" ) ||
  1876. ( $cmd eq "channelUp" ) || ( $cmd eq "CHANNELUP" ) ) {
  1877. IOWrite( $hash, "$hash->{PLAYERMAC} playlist jump %2B1\n" );
  1878. } elsif( ( $cmd eq "prev" ) || ( $cmd eq "PREV" ) || ( $cmd eq "Prev" ) ||
  1879. ( $cmd eq "channelDown" ) || ( $cmd eq "CHANNELDOWN" ) ) {
  1880. IOWrite( $hash, "$hash->{PLAYERMAC} playlist jump %2D1\n" );
  1881. } elsif( ( $cmd eq "volume" ) || ( $cmd eq "VOLUME" ) ||
  1882. ( $cmd eq "Volume" ) ||( $cmd eq "volumeStraight" ) ) {
  1883. if(( @arg != 1 )&&( @arg != 2 )) {
  1884. my $msg = "SB_PLAYER_Set: no arguments for Vol given.";
  1885. Log3( $hash, 3, $msg );
  1886. return( $msg );
  1887. }
  1888. # set the volume to the desired level. Needs to be 0..100
  1889. # no error checking here, as the server does this
  1890. if( $arg[ 0 ] <= AttrVal( $name, "volumeLimit", 100 ) ) {
  1891. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $arg[ 0 ]\n" );
  1892. # CD 0007
  1893. SB_PLAYER_SetSyncedVolume($hash,$arg[0]) if (!defined($arg[1]));
  1894. } else {
  1895. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " .
  1896. AttrVal( $name, "volumeLimit", 50 ) . "\n" );
  1897. # CD 0007
  1898. SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 )) if (!defined($arg[1]));
  1899. }
  1900. } elsif( $cmd eq $hash->{FAVSET} ) {
  1901. if ($arg[0] ne '-') { # CD 0014
  1902. if( defined( $hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{ID} ) ) {
  1903. my $fid = $hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{ID};
  1904. IOWrite( $hash, "$hash->{PLAYERMAC} favorites playlist " .
  1905. "play item_id:$fid\n" );
  1906. $hash->{FAVSELECT} = $arg[ 0 ];
  1907. readingsSingleUpdate( $hash, "$hash->{FAVSET}", "$arg[ 0 ]", 1 );
  1908. # SB_PLAYER_GetStatus( $hash ); # CD 0021 deaktiviert, zu früh
  1909. }
  1910. } # CD 0014
  1911. } elsif( ( $cmd eq "volumeUp" ) || ( $cmd eq "VOLUMEUP" ) ||
  1912. ( $cmd eq "VolumeUp" ) ) {
  1913. # increase volume
  1914. if( ( ReadingsVal( $name, "volumeStraight", 50 ) +
  1915. AttrVal( $name, "volumeStep", 10 ) ) <=
  1916. AttrVal( $name, "volumeLimit", 100 ) ) {
  1917. my $volstr = sprintf( "+%02d", AttrVal( $name, "volumeStep", 10 ) );
  1918. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $volstr\n" );
  1919. # CD 0007
  1920. SB_PLAYER_SetSyncedVolume($hash,$volstr);
  1921. } else {
  1922. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " .
  1923. AttrVal( $name, "volumeLimit", 50 ) . "\n" );
  1924. # CD 0007
  1925. SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 ));
  1926. }
  1927. } elsif( ( $cmd eq "volumeDown" ) || ( $cmd eq "VOLUMEDOWN" ) ||
  1928. ( $cmd eq "VolumeDown" ) ) {
  1929. my $volstr = sprintf( "-%02d", AttrVal( $name, "volumeStep", 10 ) );
  1930. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $volstr\n" );
  1931. # CD 0007
  1932. SB_PLAYER_SetSyncedVolume($hash,$volstr);
  1933. } elsif( ( $cmd eq "mute" ) || ( $cmd eq "MUTE" ) || ( $cmd eq "Mute" ) ) {
  1934. IOWrite( $hash, "$hash->{PLAYERMAC} mixer muting toggle\n" );
  1935. } elsif( $cmd eq "on" ) {
  1936. if( $hash->{CANPOWEROFF} eq "0" ) {
  1937. IOWrite( $hash, "$hash->{PLAYERMAC} play\n" );
  1938. } else {
  1939. IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" );
  1940. }
  1941. } elsif( $cmd eq "off" ) {
  1942. # off command to go here
  1943. if( $hash->{CANPOWEROFF} eq "0" ) {
  1944. IOWrite( $hash, "$hash->{PLAYERMAC} stop\n" );
  1945. } else {
  1946. IOWrite( $hash, "$hash->{PLAYERMAC} power 0\n" );
  1947. }
  1948. } elsif( ( $cmd eq "repeat" ) || ( $cmd eq "REPEAT" ) ||
  1949. ( $cmd eq "Repeat" ) ) {
  1950. if( @arg != 1 ) {
  1951. my $msg = "SB_PLAYER_Set: no arguments for repeat given.";
  1952. Log3( $hash, 3, $msg );
  1953. return( $msg );
  1954. }
  1955. if( $arg[ 0 ] eq "off" ) {
  1956. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 0\n" );
  1957. readingsSingleUpdate( $hash, "repeat", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  1958. } elsif( $arg[ 0 ] eq "one" ) {
  1959. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 1\n" );
  1960. readingsSingleUpdate( $hash, "repeat", "one", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  1961. } elsif( $arg[ 0 ] eq "all" ) {
  1962. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 2\n" );
  1963. readingsSingleUpdate( $hash, "repeat", "all", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  1964. } else {
  1965. my $msg = "SB_PLAYER_Set: unknown argument for repeat given.";
  1966. Log3( $hash, 3, $msg );
  1967. return( $msg );
  1968. }
  1969. } elsif( ( $cmd eq "shuffle" ) || ( $cmd eq "SHUFFLE" ) ||
  1970. ( $cmd eq "Shuffle" ) ) {
  1971. if( @arg != 1 ) {
  1972. my $msg = "SB_PLAYER_Set: no arguments for shuffle given.";
  1973. Log3( $hash, 3, $msg );
  1974. return( $msg );
  1975. }
  1976. if( $arg[ 0 ] eq "off" ) {
  1977. IOWrite( $hash, "$hash->{PLAYERMAC} playlist shuffle 0\n" );
  1978. readingsSingleUpdate( $hash, "shuffle", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  1979. } elsif(( $arg[ 0 ] eq "on" ) || ($arg[ 0 ] eq "song" )) { # CD 0017 'song' hinzugefügt
  1980. IOWrite( $hash, "$hash->{PLAYERMAC} playlist shuffle 1\n" );
  1981. readingsSingleUpdate( $hash, "shuffle", "song", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  1982. # CD 0017 start
  1983. } elsif( $arg[ 0 ] eq "album" ) {
  1984. IOWrite( $hash, "$hash->{PLAYERMAC} playlist shuffle 2\n" );
  1985. readingsSingleUpdate( $hash, "shuffle", "album", $donotnotify ) if($updateReadingsOnSet);
  1986. # CD 0017 end
  1987. } else {
  1988. my $msg = "SB_PLAYER_Set: unknown argument for shuffle given.";
  1989. Log3( $hash, 3, $msg );
  1990. return( $msg );
  1991. }
  1992. } elsif( ( $cmd eq "show" ) ||
  1993. ( $cmd eq "SHOW" ) ||
  1994. ( $cmd eq "Show" ) ) {
  1995. # set <name> show line1:text line2:text duration:ss
  1996. my $v = join( " ", @arg );
  1997. my @buf = split( "line1:", $v );
  1998. @buf = split( "line2:", $buf[ 1 ] );
  1999. my $line1 = uri_escape( $buf[ 0 ] );
  2000. @buf = split( "duration:", $buf[ 1 ] );
  2001. my $line2 = uri_escape( $buf[ 0 ] );
  2002. my $duration = $buf[ 1 ];
  2003. my $cmdstr = "$hash->{PLAYERMAC} display $line1 $line2 $duration\n";
  2004. IOWrite( $hash, $cmdstr );
  2005. } elsif( ( $cmd eq "talk" ) ||
  2006. ( $cmd eq "TALK" ) ||
  2007. ( $cmd eq "talk" ) ||
  2008. ( lc($cmd) eq "saytext" ) ) { # CD 0014 hinzugefügt
  2009. $hash->{helper}{ttsstate}=TTS_IDLE if(!defined($hash->{helper}{ttsstate}));
  2010. $hash->{helper}{ttsExtstate}=TTS_IDLE if(!defined($hash->{helper}{ttsExtstate}));
  2011. # CD 0032 - Text2Speech verwenden ?
  2012. # CD 0033 - überarbeitet
  2013. my $useText2Speech=0;
  2014. my $errMsg;
  2015. if(AttrVal( $name, "ttslink", "none" )=~m/^Text2Speech/) {
  2016. my @extTTS=split(":",AttrVal( $name, "ttslink", "none" ));
  2017. # Device überhaupt verwendbar ?
  2018. if(defined($extTTS[1]) && defined($defs{$extTTS[1]})) {
  2019. my $ttshash=$defs{$extTTS[1]};
  2020. if(defined($ttshash->{TYPE}) && (($ttshash->{TYPE} eq 'Text2SpeechSB') || (($ttshash->{TYPE} eq 'Text2Speech') && defined($ttshash->{helper}{supportsSBPlayer})))) { # CD 0048 Text2Speech (ohne SB) unterstützen
  2021. if(defined($ttshash->{ALSADEVICE}) && ($ttshash->{ALSADEVICE} eq 'SB_PLAYER')) {
  2022. if ((AttrVal($ttshash->{NAME}, "TTS_Ressource", "x") =~ m/^(Google|VoiceRSS|SVOX-pico)$/)) { # CD 0049 $ttshash korrigiert
  2023. $useText2Speech=1;
  2024. $hash->{helper}{text2speech}{name}=$extTTS[1];
  2025. $hash->{helper}{text2speech}{pathPrefix}=join(':',@extTTS[2..$#extTTS]) if defined($extTTS[2]);
  2026. # Zustand Text2Speech ?
  2027. if(defined($ttshash->{helper}{SB_PLAYER}) || (defined($ttshash->{helper}{Text2Speech}) && @{$ttshash->{helper}{Text2Speech}} > 0)) {
  2028. # Text2Speech besetzt, warten
  2029. if($hash->{helper}{ttsstate}==TTS_IDLE) {
  2030. SB_PLAYER_SetTTSState($hash,TTS_TEXT2SPEECH_BUSY,0,0);
  2031. } else {
  2032. $hash->{helper}{ttsExtstate}=TTS_EXT_TEXT2SPEECH_BUSY if($hash->{helper}{ttsExtstate}==TTS_IDLE);
  2033. }
  2034. if(defined($hash->{helper}{text2speech}{text})) {
  2035. $hash->{helper}{text2speech}{text}.=" " . join( " ", @arg );
  2036. } else {
  2037. $hash->{helper}{text2speech}{text}=join( " ", @arg );
  2038. }
  2039. return;
  2040. } else {
  2041. # Text2Speech belegen
  2042. $ttshash->{helper}{SB_PLAYER}=$name;
  2043. if($hash->{helper}{ttsstate}==TTS_IDLE) {
  2044. SB_PLAYER_SetTTSState($hash,TTS_TEXT2SPEECH_ACTIVE,0,0);
  2045. } else {
  2046. $hash->{helper}{ttsExtstate}=TTS_EXT_TEXT2SPEECH_ACTIVE;
  2047. }
  2048. fhem("set $extTTS[1] tts ".join( " ", @arg ));
  2049. return;
  2050. }
  2051. } else {
  2052. $errMsg = "SB_PLAYER_Set: ".$extTTS[1].": Text2Speech uses unsupported TTS_Ressource ".AttrVal($ttshash->{NAME}, "TTS_Ressource", "x"); # CD 0049 $ttshash korrigiert
  2053. }
  2054. } else {
  2055. $errMsg = "SB_PLAYER_Set: ".$extTTS[1].": Text2Speech uses unsupported ALSADEVICE";
  2056. }
  2057. } else {
  2058. $errMsg = "SB_PLAYER_Set: ".$extTTS[1].": unsupported Text2Speech device";
  2059. }
  2060. } else {
  2061. $errMsg = "SB_PLAYER_Set: invalid Text2Speech device";
  2062. }
  2063. }
  2064. if(defined($errMsg)) {
  2065. Log3( $hash, 1, $errMsg );
  2066. return( $errMsg );
  2067. }
  2068. # CD 0028 start - komplett überarbeitet
  2069. # prepare text
  2070. my $ttstext=join( " ", @arg );
  2071. $ttstext = AttrVal( $name, "ttsPrefix", "" )." ".$ttstext; # CD 0032
  2072. my %Sonderzeichen = ("ä" => "ae", "Ä" => "Ae", "ü" => "ue", "Ü" => "Ue", "ö" => "oe", "Ö" => "Oe", "ß" => "ss",
  2073. "é" => "e", "è" => "e", "ë" => "e", "à" => "a", "ç" => "c" );
  2074. my $Sonderzeichenkeys = join ("|", keys(%Sonderzeichen));
  2075. if (length($ttstext)==0) {
  2076. my $msg = "SB_PLAYER_Set: no text passed for synthesis.";
  2077. Log3( $hash, 3, $msg );
  2078. return( $msg );
  2079. }
  2080. $ttstext .= "." unless ($ttstext =~ m/^.+[.,?!:;]$/);
  2081. my @textlines;
  2082. my $tl='';
  2083. # CD 0033 Unterstützung für Dateien und URLs hinzugefügt, teilweise aus 00_SONOS übernommen
  2084. my $targetSpeakMP3FileDir = AttrVal( $name, "ttsMP3FileDir", "" ); # CD 0033
  2085. my $filename; # CD 0038
  2086. if (length($ttstext)>0) {
  2087. my @words=split(' ',$ttstext);
  2088. for my $w (@words) {
  2089. # CD 0033 Datei ?, teilweise aus 00_SONOS übernommen
  2090. if ($w =~ m/\|(.*)\|/) {
  2091. push(@textlines,$tl) if($tl ne '');
  2092. $tl='';
  2093. $filename = $1;
  2094. $filename = $targetSpeakMP3FileDir.'/'.$filename if ($filename !~ m/^(\/|[a-z]:)/i);
  2095. $filename = $filename.'.mp3' if ($filename !~ m/\.mp3$/i);
  2096. push(@textlines, '|'.$filename.'|');
  2097. $filename=undef;
  2098. # CD 0038 Leerzeichen in Dateinamen zulassen
  2099. } elsif ($w =~ m/^\|(.*)/) {
  2100. $filename = $1;
  2101. } elsif (($w =~ m/(.*)\|/) && defined($filename)) {
  2102. $filename .= " ".$1;
  2103. push(@textlines,$tl) if($tl ne '');
  2104. $tl='';
  2105. $filename = $targetSpeakMP3FileDir.'/'.$filename if ($filename !~ m/^(\/|[a-z]:)/i);
  2106. $filename = $filename.'.mp3' if ($filename !~ m/\.mp3$/i);
  2107. push(@textlines, '|'.$filename.'|');
  2108. $filename=undef;
  2109. # CD 0038
  2110. } else {
  2111. $w =~ s/[\\|*~<>^\n\(\)\[\]\{\}[:cntrl:]]/ /g;
  2112. $w =~ s/\s+/ /g;
  2113. $w =~ s/^\s|\s$//g;
  2114. $w =~ s/($Sonderzeichenkeys)/$Sonderzeichen{$1}/g if(AttrVal( $name, "ttslink", "x" ) !~ m/voicerss/i); # CD 0045 Sonderzeichen für VoiceRSS nicht ersetzen
  2115. $w = uri_escape( $w ) if defined($hash->{helper}{ttsOptions}{doubleescape}); # CD 0060
  2116. # CD 0032 end
  2117. if((length($tl)+length($w)+1)<100) {
  2118. $tl.=' ' if(length($tl)>0);
  2119. $tl.=$w;
  2120. } else {
  2121. push(@textlines,$tl);
  2122. $tl=$w;
  2123. }
  2124. }
  2125. }
  2126. }
  2127. push(@textlines,$tl) if($tl ne '');
  2128. if($hash->{helper}{ttsstate}==TTS_IDLE) {
  2129. # talk ist nicht aktiv
  2130. # CD 0038 Player zuerst einschalten
  2131. if(ReadingsVal($name,"power","x") ne "on") {
  2132. SB_PLAYER_SetTTSState($hash,TTS_WAITFORPOWERON,0,0);
  2133. RemoveInternalTimer( "TimeoutTTSWaitForPowerOn:$name");
  2134. if($hash->{helper}{playerStatusOK}==1) { # CD 0042
  2135. IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" );
  2136. InternalTimer( gettimeofday() + 5.00,
  2137. "SB_PLAYER_tcb_TimeoutTTSWaitForPowerOn",
  2138. "TimeoutTTSWaitForPowerOn:$name",
  2139. 0 );
  2140. $hash->{helper}{ttsPowerWasOff}=1;
  2141. # CD 0042 start
  2142. } else {
  2143. InternalTimer( gettimeofday() + 10.00,
  2144. "SB_PLAYER_tcb_TimeoutTTSWaitForPowerOn",
  2145. "TimeoutTTSWaitForPowerOn:$name",
  2146. 0 );
  2147. }
  2148. # CD 0042 end
  2149. } else {
  2150. # CD 0038 end
  2151. SB_PLAYER_PrepareTalk($hash);
  2152. }
  2153. } else {
  2154. }
  2155. for my $outstr (@textlines) {
  2156. if ($outstr =~ m/\|(.*)\|/) { # CD 0033
  2157. Log3($hash, defined($hash->{helper}{ttsOptions}{debug})?0:6,"SB_PLAYER_Set: $name: add to ttsqueue: $1"); # CD 0036
  2158. push(@{$hash->{helper}{ttsqueue}},uri_escape(decode('utf-8',$1))); # CD 0033 # CD 0038 uri_escape(decode... hinzugefügt
  2159. } else {
  2160. $outstr =~ s/\s/+/g;
  2161. # CD 0054 versuchen Format zu erraten, funktioniert nicht für telnet
  2162. if (decode("utf8",$outstr)=~m/\x{fffd}/) {
  2163. Log3($hash, defined($hash->{helper}{ttsOptions}{debug})?0:6,"SB_PLAYER_Set: $name: string is not utf-8, using iso-8859-1");
  2164. $outstr=encode('utf-8',decode("iso-8859-1",$outstr));
  2165. }
  2166. $outstr = uri_escape( $outstr );
  2167. #$outstr = uri_escape( $outstr ) if defined($hash->{helper}{ttsOptions}{doubleescape}); # CD 0059 # CD 0060 zu spät nach oben verschoben
  2168. # CD 0045
  2169. my $ttslink=AttrVal( $name, "ttslink", "" );
  2170. if(defined($ttslink)) {
  2171. # Profile
  2172. my $lang=AttrVal( $name, "ttslanguage", "de" );
  2173. if ($ttslink =~ m/voicerss/i) { # CD 0047 Sprache auch bei voicerss innerhalb der URL anpassen
  2174. $lang="de-de" if($lang eq "de");
  2175. $lang="en-us" if($lang eq "en");
  2176. $lang="fr-fr" if($lang eq "fr");
  2177. }
  2178. $ttslink="http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob" if ($ttslink eq 'http://translate.google.com/translate_tts?ie=UTF-8'); # CD 0047, CD 0048 client=tw-ob verwenden
  2179. $ttslink="http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob" if ($ttslink eq "Google"); # CD 0048 client=tw-ob verwenden
  2180. $ttslink="http://api.voicerss.org/?key=<APIKEY>&src=<TEXT>&hl=<LANG>" if ($ttslink eq "VoiceRSS");
  2181. # alte Links anpassen
  2182. if($ttslink !~ m/<TEXT>/) {
  2183. $ttslink.="&tl=<LANG>" if($ttslink !~ m/<LANG>/);
  2184. $ttslink.="&q=<TEXT>";
  2185. }
  2186. $ttslink =~ s/<LANG>/$lang/;
  2187. $ttslink =~ s/<TEXT>/$outstr/;
  2188. my $apikey=AttrVal( $name, "ttsAPIKey", undef );
  2189. if(($ttslink =~ m/<APIKEY>/) && !defined($apikey)) {
  2190. Log3($hash, 2,"SB_PLAYER_Set: $name: talk - missing API key");
  2191. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,0);
  2192. } else {
  2193. $ttslink =~ s/<APIKEY>/$apikey/;
  2194. Log3($hash, defined($hash->{helper}{ttsOptions}{debug})?0:6,"SB_PLAYER_Set: $name: add to ttsqueue: $ttslink"); # CD 0036
  2195. push(@{$hash->{helper}{ttsqueue}},$ttslink);
  2196. }
  2197. }
  2198. }
  2199. }
  2200. SB_PLAYER_LoadTalk($hash) if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST); # CD 0033 # CD 0038 auf TTS_LOADPLAYLIST prüfen
  2201. # CD 0028 end
  2202. } elsif( ( $cmd eq "playlist" ) ||
  2203. ( $cmd eq "PLAYLIST" ) ||
  2204. ( $cmd eq "Playlist" ) ) {
  2205. #if( ( @arg != 2 ) && ( @arg != 3 ) ) { # CD 0014 deaktiviert
  2206. if( @arg < 2) { # CD 0014
  2207. my $msg = "SB_PLAYER_Set: no arguments for Playlist given.";
  2208. Log3( $hash, 3, $msg );
  2209. return( $msg );
  2210. }
  2211. # CD 0014 start
  2212. if (@arg>1) {
  2213. my $outstr = uri_escape(decode('utf-8',join( " ", @arg[1..$#arg]))); # CD 0017
  2214. Log3( $hash, 5, "SB_PLAYER_Set($name): playlists command = $arg[ 0 ] param = $outstr" );
  2215. if( $arg[ 0 ] eq "track" ) {
  2216. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2217. "track.titlesearch:$outstr\n" );
  2218. } elsif( $arg[ 0 ] eq "album" ) {
  2219. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2220. "album.titlesearch:$outstr\n" );
  2221. } elsif( $arg[ 0 ] eq "artist" ) {
  2222. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2223. "contributor.namesearch:$outstr\n" ); # CD 0014 'titlesearch' durch 'namesearch' ersetzt
  2224. } elsif( $arg[ 0 ] eq "play" ) {
  2225. IOWrite( $hash, "$hash->{PLAYERMAC} playlist play $outstr\n" );
  2226. # CD 0038 start
  2227. } elsif( $arg[ 0 ] eq "add" ) {
  2228. IOWrite( $hash, "$hash->{PLAYERMAC} playlist add $outstr\n" );
  2229. } elsif( $arg[ 0 ] eq "insert" ) {
  2230. IOWrite( $hash, "$hash->{PLAYERMAC} playlist insert $outstr\n" );
  2231. # CD 0038 end
  2232. } elsif( $arg[ 0 ] eq "year" ) {
  2233. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2234. "track.year:$outstr\n" );
  2235. } elsif( $arg[ 0 ] eq "genre" ) {
  2236. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2237. "genre.namesearch:$outstr\n" );
  2238. #} elsif( $arg[ 0 ] eq "comment" ) { # CD 0014 funktioniert nicht
  2239. # IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " .
  2240. # "comments.value:$outstr\n" );
  2241. # CD 0038 loadalbum wieder hergestellt und überarbeitet
  2242. } else {
  2243. my $genre='*';
  2244. my $album='*';
  2245. my $artist='*';
  2246. my $t=0;
  2247. foreach( @arg ) {
  2248. if( $_ =~ /^(genre:)(.*)/ ) {
  2249. $genre=$2;
  2250. $t=1;
  2251. } elsif ( $_ =~ /^(album:)(.*)/ ) {
  2252. $album=$2;
  2253. $t=2;
  2254. } elsif ( $_ =~ /^(artist:)(.*)/ ) {
  2255. $artist=$2;
  2256. $t=3;
  2257. } else {
  2258. $genre.=" ".$_ if($t==1);
  2259. $album.=" ".$_ if($t==2);
  2260. $artist.=" ".$_ if($t==3);
  2261. }
  2262. }
  2263. if( $t>0 ) {
  2264. $genre=uri_escape(decode('utf-8',$genre)) if($genre ne '*');
  2265. $album=uri_escape(decode('utf-8',$album)) if($album ne '*');
  2266. $artist=uri_escape(decode('utf-8',$artist)) if($artist ne '*');
  2267. IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadalbum " .
  2268. "$genre $artist $album\n" );
  2269. }
  2270. # CD 0038 end
  2271. }
  2272. # CD 0014 end
  2273. } else {
  2274. # what the f... we checked beforehand
  2275. }
  2276. } elsif( $cmd eq "allalarms" ) {
  2277. if( $arg[ 0 ] eq "enable" ) {
  2278. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled 1\n" ); # MM 0016
  2279. readingsSingleUpdate( $hash, "alarmsEnabled", "on", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2280. } elsif( $arg[ 0 ] eq "disable" ) {
  2281. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled 0\n" ); # MM 0016
  2282. readingsSingleUpdate( $hash, "alarmsEnabled", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2283. } elsif( $arg[ 0 ] eq "statusRequest" ) {
  2284. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  2285. # CD 0016 start
  2286. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled ?\n" );
  2287. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmDefaultVolume ?\n" );
  2288. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmTimeoutSeconds ?\n" );
  2289. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmSnoozeSeconds ?\n" );
  2290. # CD 0016 end
  2291. # CD 0015 start
  2292. } elsif( $arg[ 0 ] eq "delete" ) {
  2293. $hash->{helper}{deleteAllAlarms}=1;
  2294. for(my $i=1;$i<=$hash->{helper}{ALARMSCOUNT};$i++) { # CD 0016 ALARMSCOUNT nach {helper} verschoben
  2295. IOWrite( $hash, "$hash->{PLAYERMAC} alarm delete id:". ReadingsVal($name,"alarm".$i."_id","0"). "\n" );
  2296. }
  2297. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  2298. } elsif( $arg[ 0 ] eq "add" ) {
  2299. $arg[ 0 ]="set";
  2300. SB_PLAYER_Alarm( $hash, 0, @arg );
  2301. # CD 0015 end
  2302. } else {
  2303. }
  2304. # CD 0016 start, von MM übernommen, Namen geändert
  2305. } elsif( index( $cmd, "alarms" ) != -1 ) {
  2306. if($cmd eq "alarmsSnooze") {
  2307. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmSnoozeSeconds ". $arg[0]*60 ."\n" );
  2308. readingsSingleUpdate( $hash, "alarmsSnooze", $arg[ 0 ], $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2309. } elsif($cmd eq "alarmsTimeout") {
  2310. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmTimeoutSeconds ". $arg[0]*60 ."\n" );
  2311. readingsSingleUpdate( $hash, "alarmsTimeout", $arg[ 0 ], $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2312. } elsif($cmd eq "alarmsDefaultVolume") {
  2313. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmDefaultVolume ". $arg[0] ."\n" );
  2314. readingsSingleUpdate( $hash, "alarmsDefaultVolume", $arg[ 0 ], $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2315. } elsif($cmd eq "alarmsFadeIn") {
  2316. if($arg[0] eq 'on') {
  2317. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmfadeseconds 1\n" );
  2318. readingsSingleUpdate( $hash, "alarmsFadeIn", "on", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2319. } else {
  2320. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmfadeseconds 0\n" );
  2321. readingsSingleUpdate( $hash, "alarmsFadeIn", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2322. }
  2323. } elsif($cmd eq "alarmsEnabled") {
  2324. if( $arg[ 0 ] eq "on" ) {
  2325. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled 1\n" );
  2326. readingsSingleUpdate( $hash, "alarmsEnabled", "on", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2327. } else {
  2328. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled 0\n" );
  2329. readingsSingleUpdate( $hash, "alarmsEnabled", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2330. }
  2331. }
  2332. # CD 0016
  2333. } elsif( index( $cmd, "alarm" ) != -1 ) {
  2334. my $alarmno = int( substr( $cmd, 5 ) ) + 0;
  2335. Log3( $hash, 5, "SB_PLAYER_Set: $name: alarmid:$alarmno" );
  2336. return( SB_PLAYER_Alarm( $hash, $alarmno, @arg ) );
  2337. } elsif( ( $cmd eq "sleep" ) || ( $cmd eq "SLEEP" ) ||
  2338. ( $cmd eq "Sleep" ) ) {
  2339. # split the time string up
  2340. my @buf = split( ":", $arg[ 0 ] );
  2341. if( scalar( @buf ) != 3 ) {
  2342. my $msg = "SB_PLAYER_Set: please use hh:mm:ss for sleep time.";
  2343. Log3( $hash, 3, $msg );
  2344. return( $msg );
  2345. }
  2346. my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ];
  2347. IOWrite( $hash, "$hash->{PLAYERMAC} sleep $secs\n" );
  2348. return( undef );
  2349. } elsif( ( $cmd eq "cliraw" ) || ( $cmd eq "CLIRAW" ) ||
  2350. ( $cmd eq "Cliraw" ) ) {
  2351. # write raw messages to the CLI interface per player
  2352. my $v = join( " ", @arg );
  2353. Log3( $hash, 5, "SB_PLAYER_Set: cliraw: $v " );
  2354. IOWrite( $hash, "$hash->{PLAYERMAC} $v\n" );
  2355. return( undef );
  2356. # CD 0014 start
  2357. } elsif( ( $cmd eq "save" ) || ( $cmd eq "SAVE" ) ) {
  2358. if(defined($arg[0])) {
  2359. SB_PLAYER_Save($hash, $arg[0]);
  2360. } else {
  2361. SB_PLAYER_Save($hash, "");
  2362. }
  2363. } elsif( ( $cmd eq "recall" ) || ( $cmd eq "RECALL" ) ) {
  2364. if(defined($arg[0])) {
  2365. SB_PLAYER_Recall($hash, join(" ", @arg));
  2366. } else {
  2367. SB_PLAYER_Recall($hash, "");
  2368. }
  2369. # CD 0014 end
  2370. } elsif( $cmd eq "statusRequest" ) {
  2371. RemoveInternalTimer( $hash );
  2372. SB_PLAYER_GetStatus( $hash );
  2373. } elsif( $cmd eq "sync" ) {
  2374. # CD 0018 wenn der Player bereits in einer Gruppe ist und 'new' ist vorhanden, wird der Player zuerst aus der Gruppe entfernt
  2375. if(( @arg == 2) && ($arg[1] eq "new") && ($hash->{SYNCED} eq 'yes')) {
  2376. IOWrite( $hash, "$hash->{PLAYERMAC} sync -\n" );
  2377. # CD 0028 start
  2378. if($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) {
  2379. SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,0);
  2380. }
  2381. # CD 0028 end
  2382. }
  2383. # CD 0018 end
  2384. # CD 0018 Synchronisation mehrerer Player
  2385. if(( @arg == 1 ) || ( @arg == 2)) {
  2386. my $msg;
  2387. my $dev;
  2388. my @dvs=();
  2389. my $doGetStatus=0;
  2390. @dvs=split(",",$arg[0]);
  2391. foreach (@dvs) {
  2392. my $dev=$_;
  2393. my $mac;
  2394. # CD 0018 end
  2395. if( defined( $hash->{helper}{SB_PLAYER_SyncMasters}{$dev}{MAC} ) ) {
  2396. $mac=$hash->{helper}{SB_PLAYER_SyncMasters}{$dev}{MAC};
  2397. } else {
  2398. # CD 0038 Player nicht gefunden, testen ob Name zu einem FHEM-Gerät passt
  2399. if(defined($defs{$dev}) && defined($defs{$dev}{TYPE}) && ($defs{$dev}{TYPE} eq 'SB_PLAYER') && defined($defs{$dev}{PLAYERMAC})) {
  2400. $mac=$defs{$dev}{PLAYERMAC};
  2401. } else {
  2402. # CD 0038 end
  2403. my $msg = "SB_PLAYER_Set: no MAC for player ".$dev.".";
  2404. Log3( $hash, 3, $msg );
  2405. #return( $msg ); # CD 0018 wenn keine MAC vorhanden weitermachen
  2406. }
  2407. }
  2408. if(defined($mac)) {
  2409. # CD 0038 wenn asSlave angegeben ist, wird der erste Player zur Gruppe des 2. hinzugefügt
  2410. if((@arg == 2) && ($arg[1] eq "asSlave")) {
  2411. IOWrite( $hash, "$mac sync " .
  2412. "$hash->{PLAYERMAC}\n" );
  2413. } else {
  2414. # CD 0038 end
  2415. IOWrite( $hash, "$hash->{PLAYERMAC} sync " .
  2416. "$mac\n" );
  2417. }
  2418. $doGetStatus=1;
  2419. }
  2420. } # CD 0018
  2421. SB_PLAYER_GetStatus( $hash ) if($doGetStatus==1);
  2422. }
  2423. # CD 0018 end
  2424. } elsif( $cmd eq "unsync" ) {
  2425. IOWrite( $hash, "$hash->{PLAYERMAC} sync -\n" );
  2426. SB_PLAYER_GetStatus( $hash );
  2427. # CD 0028 start
  2428. if($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) {
  2429. SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,0);
  2430. }
  2431. # CD 0028 end
  2432. } elsif( $cmd eq "playlists" ) {
  2433. if( @arg == 1 ) {
  2434. my $msg;
  2435. if( defined( $hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID} ) ) {
  2436. $msg = "$hash->{PLAYERMAC} playlistcontrol cmd:load " .
  2437. "playlist_id:$hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID}";
  2438. Log3( $hash, 5, "SB_PLAYER_Set($name): playlists command = " .
  2439. $msg . " ........ with $arg[0]" );
  2440. IOWrite( $hash, $msg . "\n" );
  2441. readingsSingleUpdate( $hash, "playlists", "$arg[ 0 ]", 1 );
  2442. SB_PLAYER_GetStatus( $hash );
  2443. } else {
  2444. $msg = "SB_PLAYER_Set: no name for playlist defined.";
  2445. Log3( $hash, 3, $msg );
  2446. return( $msg );
  2447. }
  2448. } else {
  2449. my $msg = "SB_PLAYER_Set: no arguments for playlists given.";
  2450. Log3( $hash, 3, $msg );
  2451. return( $msg );
  2452. }
  2453. } elsif( $cmd eq "resetTTS" ) {
  2454. delete($hash->{helper}{saveLocked}) if (defined($hash->{helper}{saveLocked})); # CD 0056
  2455. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  2456. # CD 0047 start
  2457. } elsif( lc($cmd) eq "currenttrackposition" ) {
  2458. if(defined($arg[0])) {
  2459. IOWrite( $hash, "$hash->{PLAYERMAC} time $arg[0]\n" );
  2460. }
  2461. # CD 0047 end
  2462. # CD 0052 start
  2463. } elsif( lc($cmd) eq "snooze" ) {
  2464. IOWrite( $hash, "$hash->{PLAYERMAC} jivealarm snooze:1\n" );
  2465. # CD 0052 end
  2466. } else {
  2467. my $msg = "SB_PLAYER_Set: unsupported command given";
  2468. Log3( $hash, 3, $msg );
  2469. return( $msg );
  2470. }
  2471. return( undef );
  2472. }
  2473. # CD 0033 hinzugefügt
  2474. # ----------------------------------------------------------------------------
  2475. # add talk segments to playlist
  2476. # ----------------------------------------------------------------------------
  2477. sub SB_PLAYER_LoadTalk($) {
  2478. # gespeicherte Elemente in Playlist einfügen
  2479. my ( $hash ) = @_;
  2480. my $name = $hash->{NAME};
  2481. if(defined($hash->{helper}{ttsqueue})) {
  2482. if(($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST)||($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE)) {
  2483. IOWrite( $hash, "$hash->{PLAYERMAC} playlist clear\n" ) if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST);
  2484. for (@{$hash->{helper}{ttsqueue}}) {
  2485. if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST) {
  2486. # ich bin Master und talk ist nicht aktiv
  2487. IOWrite( $hash, "$hash->{PLAYERMAC} playlist add " . $_ . "\n" );
  2488. } else {
  2489. # talk ist aktiv und ein anderer Player ist Master
  2490. IOWrite( $hash, $hash->{helper}{ttsMaster}." fhemrelay ttsadd ".$_."\n" );
  2491. }
  2492. }
  2493. delete($hash->{helper}{ttsqueue});
  2494. if($hash->{helper}{ttsstate}!=TTS_SYNCGROUPACTIVE) {
  2495. # andere Player in Gruppe informieren
  2496. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  2497. my @pl=split(",",$hash->{SYNCGROUP}.",".$hash->{SYNCMASTER});
  2498. foreach (@pl) {
  2499. if ($hash->{PLAYERMAC} ne $_) {
  2500. IOWrite( $hash, "$_ fhemrelay ttsactive ".$hash->{PLAYERMAC}."\n" );
  2501. IOWrite( $hash, "$_ fhemrelay ttsforcegroupon\n" ) if(defined($hash->{helper}{ttsOptions}{forcegroupon})); # CD 0030
  2502. }
  2503. }
  2504. }
  2505. }
  2506. } else {
  2507. # talk ist aktiv und ich bin Master
  2508. # warten bis stop
  2509. }
  2510. }
  2511. }
  2512. # CD 0033 hinzugefügt
  2513. # ----------------------------------------------------------------------------
  2514. # prepare player for talk
  2515. # ----------------------------------------------------------------------------
  2516. sub SB_PLAYER_PrepareTalk($) {
  2517. # kein readingsBulkUpdate
  2518. # aktuellen Stand abspeichern, playlist löschen, Lautstärke setzen
  2519. my ( $hash ) = @_;
  2520. my $name = $hash->{NAME};
  2521. # talk ist nicht aktiv
  2522. if(!defined($hash->{helper}{ttsOptions}{nosaverestore})) {
  2523. SB_PLAYER_SetTTSState($hash,TTS_SAVE,0,0);
  2524. SB_PLAYER_Save( $hash, "xxTTSxx" ) if(!defined($hash->{helper}{saveLocked}));
  2525. }
  2526. $hash->{helper}{saveLocked}=1;
  2527. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 0\n" );
  2528. IOWrite( $hash, "$hash->{PLAYERMAC} playlist clear\n" );
  2529. if(defined($hash->{helper}{ttsVolume})) {
  2530. SB_PLAYER_SetTTSState($hash,TTS_SETVOLUME,0,0);
  2531. my $vol=$hash->{helper}{ttsVolume};
  2532. $vol=AttrVal( $name, "volumeLimit", 100 ) if(( $hash->{helper}{ttsVolume} > AttrVal( $name, "volumeLimit", 100 ) )&&!defined($hash->{helper}{ttsOptions}{ignorevolumelimit})); # CD 0031
  2533. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".$vol."\n" );
  2534. SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{ttsVolume});
  2535. }
  2536. SB_PLAYER_SetTTSState($hash,TTS_LOADPLAYLIST,0,0);
  2537. }
  2538. # CD 0014 start
  2539. # ----------------------------------------------------------------------------
  2540. # recall player state
  2541. #
  2542. # CD 0036 added $statename
  2543. # ----------------------------------------------------------------------------
  2544. sub SB_PLAYER_Recall($$) {
  2545. my ( $hash, $arg ) = @_; # CD 0036
  2546. my $name = $hash->{NAME};
  2547. # CD 0036 start
  2548. my $del=0;
  2549. my $delonly=0;
  2550. my $forceoff=0;
  2551. my $forceon=0;
  2552. my $forceplay=0;
  2553. my $forcestop=0;
  2554. my $statename;
  2555. my @args=split " ",$arg;
  2556. if(defined($args[0])) {
  2557. $statename=$args[0];
  2558. } else {
  2559. $statename='default';
  2560. }
  2561. # Optionen auswerten
  2562. for my $opt (@args) {
  2563. $del=1 if($opt=~ m/^del$/);
  2564. $delonly=1 if($opt=~ m/^delonly$/);
  2565. $forceoff=1 if($opt=~ m/^off$/);
  2566. $forceon=1 if(($opt=~ m/^on$/)||($opt=~ m/^play$/));
  2567. $forceplay=1 if($opt=~ m/^play$/);
  2568. $forcestop=1 if($opt=~ m/^stop$/);
  2569. }
  2570. $forceoff=0 if($forceon==1);
  2571. $forcestop=0 if($forceplay==1);
  2572. # CD 0036 end
  2573. # wurde überhaupt etwas gespeichert ?
  2574. if(defined($hash->{helper}{savedPlayerState}) && defined($hash->{helper}{savedPlayerState}{$statename})) {
  2575. if($delonly==0) {
  2576. # CD 0029 start
  2577. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2578. Log3( $hash, 0, "SB_PLAYER_Recall: $name: restoring...");
  2579. $hash->{helper}{ttsOptions}{logloaddone}=1;
  2580. $hash->{helper}{ttsOptions}{logplay}=1;
  2581. }
  2582. # CD 0029 end
  2583. IOWrite( $hash, "$hash->{PLAYERMAC} playlist shuffle 0\n");
  2584. if (defined($hash->{helper}{savedPlayerState}{$statename}{playlistIds})) {
  2585. # wegen Shuffle Playlist neu erzeugen
  2586. # CD 0030 start
  2587. IOWrite( $hash, "$hash->{PLAYERMAC} playlist clear\n");
  2588. my @playlistIds=split(',',$hash->{helper}{savedPlayerState}{$statename}{playlistIds});
  2589. my $f=0; # CD 0048
  2590. for my $id (@playlistIds) {
  2591. if($id>=0) {
  2592. IOWrite( $hash, "$hash->{PLAYERMAC} playlistcontrol cmd:add track_id:".$id."\n");
  2593. } else {
  2594. if(defined($hash->{helper}{savedPlayerState}{$statename}{playlistUrls}) && defined($hash->{helper}{savedPlayerState}{$statename}{playlistUrls}{$id})) {
  2595. if (defined($hash->{helper}{savedPlayerState}{$statename}{path}) && ($f==0)) { # CD 0048
  2596. IOWrite( $hash, "$hash->{PLAYERMAC} playlist add ".$hash->{helper}{savedPlayerState}{$statename}{path}."\n"); # CD 0048
  2597. } else { # CD 0048
  2598. IOWrite( $hash, "$hash->{PLAYERMAC} playlist add ".$hash->{helper}{savedPlayerState}{$statename}{playlistUrls}{$id}."\n");
  2599. } # CD 0048
  2600. } else {
  2601. Log3( $hash, 2, "SB_PLAYER_Recall: $name: no url found for id ".$id);
  2602. }
  2603. }
  2604. $f=1; # CD 0048
  2605. }
  2606. IOWrite( $hash, "$hash->{PLAYERMAC} playlist index ".$hash->{helper}{savedPlayerState}{$statename}{playlistCurrentTrack}."\n");
  2607. # CD 0030 end
  2608. } else {
  2609. # auf dem Server gespeicherte Playlist fortsetzen
  2610. if( $hash->{helper}{savedPlayerState}{$statename}{playStatus} eq "playing" ) {
  2611. IOWrite( $hash, "$hash->{PLAYERMAC} playlist resume fhem_$hash->{NAME}_$statename\n" );
  2612. } else {
  2613. IOWrite( $hash, "$hash->{PLAYERMAC} playlist resume fhem_$hash->{NAME}_$statename noplay:1\n" );
  2614. }
  2615. }
  2616. if ($hash->{helper}{savedPlayerState}{$statename}{volumeStraight} ne '?') {
  2617. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $hash->{helper}{savedPlayerState}{$statename}{volumeStraight}\n" );
  2618. SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{savedPlayerState}{$statename}{volumeStraight});
  2619. }
  2620. if ($hash->{helper}{savedPlayerState}{$statename}{repeat} ne ReadingsVal($name,"repeat","?")) {
  2621. if( $hash->{helper}{savedPlayerState}{$statename}{repeat} eq "off" ) {
  2622. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 0\n" );
  2623. } elsif( $hash->{helper}{savedPlayerState}{$statename}{repeat} eq "one" ) {
  2624. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 1\n" );
  2625. } elsif( $hash->{helper}{savedPlayerState}{$statename}{repeat} eq "all" ) {
  2626. IOWrite( $hash, "$hash->{PLAYERMAC} playlist repeat 2\n" );
  2627. }
  2628. }
  2629. # CD 0028 start
  2630. if ((($hash->{helper}{savedPlayerState}{$statename}{power} eq "off") && ($forceon!=1))||($forceoff==1)) { # CD 0036 added $forceon and $forceoff
  2631. IOWrite( $hash, "$hash->{PLAYERMAC} power 0\n" );
  2632. if($hash->{helper}{ttsstate}==TTS_RESTORE) {
  2633. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  2634. delete($hash->{helper}{saveLocked}) if (defined($hash->{helper}{saveLocked})); # CD 0056
  2635. }
  2636. } else {
  2637. # CD 0028 end
  2638. if ((($hash->{helper}{savedPlayerState}{$statename}{playStatus} eq "stopped" ) && ($forceplay!=1))||($forcestop==1)) { # CD 0036 added $forceplay and $forcestop
  2639. IOWrite( $hash, "$hash->{PLAYERMAC} stop\n" );
  2640. # CD 0028 start
  2641. if($hash->{helper}{ttsstate}==TTS_RESTORE) {
  2642. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  2643. delete($hash->{helper}{saveLocked}) if (defined($hash->{helper}{saveLocked})); # CD 0056
  2644. }
  2645. # CD 0028 end
  2646. } elsif(( $hash->{helper}{savedPlayerState}{$statename}{playStatus} eq "playing" )||($forceplay==1)) { # CD 0036 added $forceplay
  2647. my @secbuf = split(',',AttrVal( $name, "fadeinsecs", '10,10' )); # CD 0050 split hinzugefügt
  2648. IOWrite( $hash, "$hash->{PLAYERMAC} play ".$secbuf[0]."\n" );
  2649. IOWrite( $hash, "$hash->{PLAYERMAC} time $hash->{helper}{savedPlayerState}{$statename}{elapsedTime}\n" ) if(defined($hash->{helper}{savedPlayerState}{$statename}{elapsedTime}));
  2650. # CD 0028 start
  2651. if($hash->{helper}{ttsstate}==TTS_RESTORE) {
  2652. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  2653. }
  2654. # CD 0028 end
  2655. } elsif( $hash->{helper}{savedPlayerState}{$statename}{playStatus} eq "paused" ) {
  2656. # paused kann nicht aus stop erreicht werden -> Playlist starten und dann pausieren
  2657. $hash->{helper}{recallPause}=1;
  2658. $hash->{helper}{recallPending}=1;
  2659. $hash->{helper}{recallPendingElapsedTime}=$hash->{helper}{savedPlayerState}{$statename}{elapsedTime}; # CD 0047
  2660. }
  2661. }
  2662. # CD 0028 restore names
  2663. readingsSingleUpdate( $hash,"playlists", $hash->{helper}{savedPlayerState}{$statename}{playlist},(AttrVal($name, "donotnotify", "true") eq "true")?0:1) if(defined($hash->{helper}{savedPlayerState}{$statename}{playlist}));
  2664. readingsSingleUpdate( $hash,"favorites", $hash->{helper}{savedPlayerState}{$statename}{favorite},(AttrVal($name, "donotnotify", "true") eq "true")?0:1) if(defined($hash->{helper}{savedPlayerState}{$statename}{favorite}));
  2665. }
  2666. delete($hash->{helper}{savedPlayerState}{$statename}) if(($del==1)||($delonly==1));
  2667. } else {
  2668. # CD 0056 start
  2669. if($hash->{helper}{ttsstate}==TTS_RESTORE) {
  2670. SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1);
  2671. delete($hash->{helper}{saveLocked}) if (defined($hash->{helper}{saveLocked}));
  2672. }
  2673. # CD 0056 end
  2674. }
  2675. }
  2676. sub SB_PLAYER_SetTTSState($$$$) {
  2677. my ( $hash, $state, $bulk, $broadcast ) = @_;
  2678. my $name = $hash->{NAME};
  2679. return if($state eq $hash->{helper}{ttsstate});
  2680. $hash->{helper}{ttsstate}=$state;
  2681. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_SetTTSState: $name: ttsstate: ".$ttsstates{$hash->{helper}{ttsstate}} );
  2682. if($bulk==1) {
  2683. readingsBulkUpdate( $hash,"talkStatus", $ttsstates{$hash->{helper}{ttsstate}} );
  2684. } else {
  2685. readingsSingleUpdate( $hash,"talkStatus", $ttsstates{$hash->{helper}{ttsstate}},(AttrVal($name, "donotnotify", "true") eq "true")?0:1);
  2686. }
  2687. if($broadcast==1) {
  2688. if($state==TTS_IDLE) {
  2689. if (defined($hash->{SYNCGROUP}) && ($hash->{SYNCGROUP} ne '?') && ($hash->{SYNCMASTER} ne 'none')) {
  2690. my @pl=split(",",$hash->{SYNCGROUP}.",".$hash->{SYNCMASTER});
  2691. foreach (@pl) {
  2692. if ($hash->{PLAYERMAC} ne $_) {
  2693. IOWrite( $hash, "$_ fhemrelay ttsidle\n" );
  2694. }
  2695. }
  2696. }
  2697. }
  2698. }
  2699. delete($hash->{helper}{ttsqueue}) if(defined($hash->{helper}{ttsqueue}) && ($state==TTS_IDLE));
  2700. delete($hash->{helper}{ttsPowerWasOff}) if (defined($hash->{helper}{ttsPowerWasOff}) && ($state==TTS_IDLE));
  2701. }
  2702. # ----------------------------------------------------------------------------
  2703. # save player state
  2704. #
  2705. # CD 0036 added $statename
  2706. # ----------------------------------------------------------------------------
  2707. sub SB_PLAYER_Save($$) {
  2708. my ( $hash, $statename ) = @_;
  2709. my $name = $hash->{NAME};
  2710. $statename='default' unless defined($statename);
  2711. delete($hash->{helper}{savedPlayerState}{$statename}) if(defined($hash->{helper}{savedPlayerState}) && defined($hash->{helper}{savedPlayerState}{$statename}));
  2712. SB_PLAYER_EstimateElapsedTime($hash);
  2713. $hash->{helper}{savedPlayerState}{$statename}{power}=ReadingsVal($name,"power","on"); # CD 0028
  2714. $hash->{helper}{savedPlayerState}{$statename}{power}="off" if(($statename eq "xxTTSxx") && defined($hash->{helper}{ttsPowerWasOff})); # CD 0038
  2715. $hash->{helper}{savedPlayerState}{$statename}{SYNCGROUP}=$hash->{SYNCGROUP} if(defined($hash->{SYNCGROUP})); # CD 0028
  2716. $hash->{helper}{savedPlayerState}{$statename}{SYNCMASTER}=$hash->{SYNCMASTER} if(defined($hash->{SYNCMASTER})); # CD 0028
  2717. $hash->{helper}{savedPlayerState}{$statename}{elapsedTime}=$hash->{helper}{elapsedTime}{VAL};
  2718. $hash->{helper}{savedPlayerState}{$statename}{playlistCurrentTrack}=ReadingsVal($name,"playlistCurrentTrack",1)-1;
  2719. $hash->{helper}{savedPlayerState}{$statename}{playStatus}=ReadingsVal($name,"playStatus","?");
  2720. $hash->{helper}{savedPlayerState}{$statename}{repeat}=ReadingsVal($name,"repeat","?");
  2721. $hash->{helper}{savedPlayerState}{$statename}{volumeStraight}=ReadingsVal($name,"volumeStraight","?");
  2722. $hash->{helper}{savedPlayerState}{$statename}{playlist}=ReadingsVal($name,"playlists","-");
  2723. $hash->{helper}{savedPlayerState}{$statename}{favorite}=ReadingsVal($name,"favorites","-");
  2724. $hash->{helper}{savedPlayerState}{$statename}{path}=$hash->{helper}{path}; # CD 0048
  2725. # CD 0029 start
  2726. delete($hash->{helper}{ttsOptions}{logloaddone}) if(defined($hash->{helper}{ttsOptions}{logloaddone}));
  2727. delete($hash->{helper}{ttsOptions}{logplay}) if(defined($hash->{helper}{ttsOptions}{logplay}));
  2728. # CD 0029 end
  2729. # CD 0036 immer intern speichern
  2730. $hash->{helper}{savedPlayerState}{$statename}{playlistIds}=$hash->{helper}{playlistIds};
  2731. $hash->{helper}{savedPlayerState}{$statename}{playlistUrls}=$hash->{helper}{playlistUrls};
  2732. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2733. Log3( $hash, 0, "SB_PLAYER_Save: $name: saving {helper}{playlistIds}: ".$hash->{helper}{playlistIds}) if(defined($hash->{helper}{playlistIds}));
  2734. }
  2735. # CD 0036
  2736. # CD 0036, alte Version, deaktivert
  2737. # ---------------------------------
  2738. if(1==0) {
  2739. # nur 1 Track -> playlist save verwenden
  2740. if((ReadingsVal($name,"playlistTracks",1)<=1)&&(!defined($hash->{helper}{ttsOptions}{internalsave}))) {
  2741. IOWrite( $hash, "$hash->{PLAYERMAC} playlist save fhem_$hash->{NAME}_$statename silent:1\n" );
  2742. # CD 0029 start
  2743. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2744. Log3( $hash, 0, "SB_PLAYER_Save: $name: 1 track in playlist, using playlist save");
  2745. }
  2746. # CD 0029 end
  2747. } else {
  2748. # mehr als 1 Track, auf shuffle prüfen (playlist resume funktioniert nicht richtig wenn shuffle nicht auf off steht)
  2749. # bei negativen Ids (Remote-Streams) und shuffle geht die vorherige Reihenfolge verloren, kein Workaround bekannt
  2750. # es werden maximal 500 Ids gespeichert, bei mehr als 500 Einträgen in der Playlists geht die vorherige Reihenfolge verloren (zu ändern)
  2751. if (( (ReadingsVal($name,"shuffle","?") eq "off") ||
  2752. (!defined($hash->{helper}{playlistIds})) ||
  2753. ($hash->{helper}{playlistIds}=~ /-/) ||
  2754. (ReadingsVal($name,"playlistTracks",0)>500)) && !defined($hash->{helper}{ttsOptions}{internalsave})) {
  2755. IOWrite( $hash, "$hash->{PLAYERMAC} playlist save fhem_$hash->{NAME}_$statename silent:1\n" );
  2756. # CD 0029 start
  2757. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2758. Log3( $hash, 0, "SB_PLAYER_Save: $name: multiple tracks in playlist, using playlist save");
  2759. }
  2760. # CD 0029 end
  2761. } else {
  2762. $hash->{helper}{savedPlayerState}{$statename}{playlistIds}=$hash->{helper}{playlistIds};
  2763. $hash->{helper}{savedPlayerState}{$statename}{playlistUrls}=$hash->{helper}{playlistUrls};
  2764. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2765. # CD 0029 start
  2766. if(defined($hash->{helper}{ttsOptions}{internalsave})) {
  2767. Log3( $hash, 0, "SB_PLAYER_Save: $name: forcing {helper}{playlistIds}: ".$hash->{helper}{playlistIds}) if(defined($hash->{helper}{playlistIds})); # CD 0033 if added
  2768. #Log3( $hash, 0, "SB_PLAYER_Save: $name: warning - negative playlist ids cannot be restored") if ($hash->{helper}{playlistIds}=~ /-/);
  2769. } else {
  2770. Log3( $hash, 0, "SB_PLAYER_Save: $name: multiple tracks in playlist, shuffle active, using {helper}{playlistIds} (".$hash->{helper}{playlistIds}.")");
  2771. }
  2772. # CD 0029 end
  2773. }
  2774. }
  2775. }
  2776. }
  2777. # ---------------------------------
  2778. # CD 0029 start
  2779. if(($hash->{helper}{ttsstate}!=TTS_IDLE) && defined($hash->{helper}{ttsOptions}{debugsaverestore})) {
  2780. Log3( $hash, 0, "SB_PLAYER_Save: $name: power ".$hash->{helper}{savedPlayerState}{$statename}{power} );
  2781. Log3( $hash, 0, "SB_PLAYER_Save: $name: elapsedTime ".$hash->{helper}{savedPlayerState}{$statename}{elapsedTime} ) if (defined($hash->{helper}{savedPlayerState}{$statename}{elapsedTime} ));
  2782. Log3( $hash, 0, "SB_PLAYER_Save: $name: playlistCurrentTrack ".$hash->{helper}{savedPlayerState}{$statename}{playlistCurrentTrack} );
  2783. Log3( $hash, 0, "SB_PLAYER_Save: $name: playStatus ".$hash->{helper}{savedPlayerState}{$statename}{playStatus} );
  2784. Log3( $hash, 0, "SB_PLAYER_Save: $name: repeat ".$hash->{helper}{savedPlayerState}{$statename}{repeat} );
  2785. Log3( $hash, 0, "SB_PLAYER_Save: $name: volumeStraight ".$hash->{helper}{savedPlayerState}{$statename}{volumeStraight} );
  2786. Log3( $hash, 0, "SB_PLAYER_Save: $name: playlist ".$hash->{helper}{savedPlayerState}{$statename}{playlist} );
  2787. Log3( $hash, 0, "SB_PLAYER_Save: $name: favorite ".$hash->{helper}{savedPlayerState}{$statename}{favorite} );
  2788. }
  2789. # CD 0029 end
  2790. }
  2791. # CD 0014 end
  2792. # ----------------------------------------------------------------------------
  2793. # set Alarms of the Player
  2794. # ----------------------------------------------------------------------------
  2795. # CD 0015 angepasst für größere Anzahl an Alarmen
  2796. sub SB_PLAYER_Alarm( $$@ ) {
  2797. my ( $hash, $n, @arg ) = @_;
  2798. my $name = $hash->{NAME};
  2799. # CD 0015 deaktiviert
  2800. #if( ( $n != 1 ) && ( $n != 2 ) ) {
  2801. # Log3( $hash, 1, "SB_PLAYER_Alarm: $name: wrong ID given. Must be 1|2" );
  2802. # return;
  2803. #}
  2804. my $id = ReadingsVal( "$name", "alarm".$n."_id", "none" ); # CD 0015 angepasst
  2805. my $updateReadingsOnSet=AttrVal($name, "updateReadingsOnSet", false); # CD 0017
  2806. my $donotnotify=AttrVal($name, "donotnotify", true); # CD 0017
  2807. Log3( $hash, 5, "SB_PLAYER_Alarm: $name: ID:$id, N:$n" );
  2808. my $cmdstr = "";
  2809. if( $arg[ 0 ] eq "set" ) {
  2810. # set <name> alarm set 0..6 hh:mm:ss playlist
  2811. if( ( @arg != 4 ) && ( @arg != 3 ) ) {
  2812. my $msg = "SB_PLAYER_Set: not enough arguments for alarm given.";
  2813. Log3( $hash, 3, $msg );
  2814. return( $msg );
  2815. }
  2816. if( $id ne "none" ) {
  2817. IOWrite( $hash, "$hash->{PLAYERMAC} alarm delete id:$id\n" ); # CD 0020 'id' fehlt
  2818. # readingsSingleUpdate( $hash, "alarmid$n", "none", 0 ); # CD 0015 deaktiviert
  2819. }
  2820. my $dow = SB_PLAYER_CheckWeekdays($arg[ 1 ]); # CD 0016 hinzugefügt
  2821. # split the time string up
  2822. my @buf = split( ":", $arg[ 2 ] );
  2823. $buf[ 2 ] = 0 if( scalar( @buf ) == 2 ); # CD 0016, von MM übernommen, geändert
  2824. if( scalar( @buf ) != 3 ) {
  2825. my $msg = "SB_PLAYER_Set: please use hh:mm:ss for alarm time.";
  2826. Log3( $hash, 3, $msg );
  2827. return( $msg );
  2828. }
  2829. my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ];
  2830. $cmdstr = "$hash->{PLAYERMAC} alarm add dow:$dow repeat:0 enabled:1";
  2831. if( defined( $arg[ 3 ] ) ) {
  2832. # CD 0015 start
  2833. my $url=join( " ", @arg[3..$#arg]);
  2834. if (defined($hash->{helper}{alarmPlaylists})) {
  2835. foreach my $e ( keys %{$hash->{helper}{alarmPlaylists}} ) {
  2836. if($url eq $hash->{helper}{alarmPlaylists}{$e}{title}) {
  2837. $url=$hash->{helper}{alarmPlaylists}{$e}{url};
  2838. last;
  2839. }
  2840. }
  2841. }
  2842. # CD 0015 end
  2843. # CD 0034 überprüfen ob gültige url, wenn nicht, versuchen file:// anzuhängen
  2844. if($url !~ /:\/\//) {
  2845. $url='/' . $url if ($url =~ /^[a-zA-Z]:\\/); # für Windows
  2846. $url=~ s/\\/\//g;
  2847. $url='file://' . $url;
  2848. }
  2849. # CD 0034 end
  2850. $cmdstr .= " url:" . uri_escape(decode('utf-8',$url)); # CD 0015 uri_escape und join hinzugefügt # CD 0020 decode hinzugefügt # CD 0034 playlist: in url: geändert
  2851. }
  2852. $cmdstr .= " time:$secs\n";
  2853. IOWrite( $hash, $cmdstr );
  2854. $hash->{LASTALARM} = $n;
  2855. } elsif(( $arg[ 0 ] eq "enable" )||( $arg[ 0 ] eq "on" )) { # CD 0015 'on' hinzugefügt
  2856. if( $id ne "none" ) {
  2857. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2858. $cmdstr .= "enabled:1\n";
  2859. IOWrite( $hash, $cmdstr );
  2860. readingsSingleUpdate( $hash, "alarm".$n."_state", "on", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2861. }
  2862. } elsif(( $arg[ 0 ] eq "disable" )||( $arg[ 0 ] eq "off" )) { # CD 0015 'off' hinzugefügt
  2863. if( $id ne "none" ) {
  2864. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2865. $cmdstr .= "enabled:0\n";
  2866. IOWrite( $hash, $cmdstr );
  2867. readingsSingleUpdate( $hash, "alarm".$n."_state", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2868. }
  2869. } elsif( $arg[ 0 ] eq "volume" ) {
  2870. if( $id ne "none" ) {
  2871. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2872. $cmdstr .= "volume:" . $arg[ 1 ] . "\n";
  2873. IOWrite( $hash, $cmdstr );
  2874. readingsSingleUpdate( $hash, "alarm".$n."_volume", $arg[ 1 ], $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2875. }
  2876. # CD 0015 start
  2877. } elsif( $arg[ 0 ] eq "sound" ) {
  2878. if( $id ne "none" ) {
  2879. if( defined($arg[ 1 ]) ) {
  2880. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2881. my $url=join( " ", @arg[1..$#arg]);
  2882. readingsSingleUpdate( $hash, "alarm".$n."_sound", $url, $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2883. if (defined($hash->{helper}{alarmPlaylists})) {
  2884. foreach my $e ( keys %{$hash->{helper}{alarmPlaylists}} ) {
  2885. if($url eq $hash->{helper}{alarmPlaylists}{$e}{title}) {
  2886. $url=$hash->{helper}{alarmPlaylists}{$e}{url};
  2887. last;
  2888. }
  2889. }
  2890. }
  2891. # CD 0034 überprüfen ob gültige url, wenn nicht, versuchen file:// anzuhängen
  2892. if($url !~ /:\/\//) {
  2893. $url='/' . $url if ($url =~ /^[a-zA-Z]:\\/); # für Windows
  2894. $url=~ s/\\/\//g;
  2895. $url='file://' . $url;
  2896. }
  2897. # CD 0034 end
  2898. $cmdstr .= "url:" . uri_escape(decode('utf-8',$url)); # CD 0017 decode hinzugefügt # CD 0034 playlist: in url: geändert
  2899. IOWrite( $hash, $cmdstr . "\n" ); # CD 0017 reaktiviert # CD 0034 \n hinzugefügt
  2900. } else {
  2901. my $msg = "SB_PLAYER_Set: alarm, no value for sound.";
  2902. Log3( $hash, 3, $msg );
  2903. return( $msg );
  2904. }
  2905. }
  2906. } elsif( $arg[ 0 ] eq "repeat" ) {
  2907. if( $id ne "none" ) {
  2908. if( defined($arg[ 1 ]) ) {
  2909. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2910. if(($arg[ 1 ] eq "1")||($arg[ 1 ] eq "on")||($arg[ 1 ] eq "yes")) {
  2911. $cmdstr .= "repeat:1\n";
  2912. readingsSingleUpdate( $hash, "alarm".$n."_repeat", "on", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2913. } else {
  2914. $cmdstr .= "repeat:0\n";
  2915. readingsSingleUpdate( $hash, "alarm".$n."_repeat", "off", $donotnotify ) if($updateReadingsOnSet); # CD 0017
  2916. }
  2917. IOWrite( $hash, $cmdstr );
  2918. } else {
  2919. my $msg = "SB_PLAYER_Set: alarm, no value for repeat.";
  2920. Log3( $hash, 3, $msg );
  2921. return( $msg );
  2922. }
  2923. }
  2924. } elsif( $arg[ 0 ] eq "wdays" ) {
  2925. if( $id ne "none" ) {
  2926. if( defined($arg[ 1 ]) ) {
  2927. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2928. my $dow=SB_PLAYER_CheckWeekdays(join( "", @arg[1..$#arg])); # CD 0017
  2929. $cmdstr .= "dow:" . $dow . "\n"; # CD 0016 SB_PLAYER_CheckWeekdays verwenden
  2930. IOWrite( $hash, $cmdstr );
  2931. # CD 0017 start
  2932. if($updateReadingsOnSet) {
  2933. my $rdaystr="";
  2934. if ($dow ne "") {
  2935. $rdaystr = "Mo" if( index( $dow, "1" ) != -1 );
  2936. $rdaystr .= " Tu" if( index( $dow, "2" ) != -1 );
  2937. $rdaystr .= " We" if( index( $dow, "3" ) != -1 );
  2938. $rdaystr .= " Th" if( index( $dow, "4" ) != -1 );
  2939. $rdaystr .= " Fr" if( index( $dow, "5" ) != -1 );
  2940. $rdaystr .= " Sa" if( index( $dow, "6" ) != -1 );
  2941. $rdaystr .= " Su" if( index( $dow, "0" ) != -1 );
  2942. } else {
  2943. $rdaystr = "none";
  2944. }
  2945. $rdaystr =~ s/^\s+|\s+$//g;
  2946. readingsSingleUpdate( $hash, "alarm".$n."_wdays", $rdaystr, $donotnotify );
  2947. }
  2948. # CD 0017 end
  2949. } else {
  2950. my $msg = "SB_PLAYER_Set: no weekdays specified for alarm.";
  2951. Log3( $hash, 3, $msg );
  2952. return( $msg );
  2953. }
  2954. }
  2955. } elsif( $arg[ 0 ] eq "time" ) {
  2956. if( $id ne "none" ) {
  2957. # split the time string up
  2958. if( !defined($arg[ 1 ]) ) {
  2959. my $msg = "SB_PLAYER_Set: no alarm time given.";
  2960. Log3( $hash, 3, $msg );
  2961. return( $msg );
  2962. }
  2963. my @buf = split( ":", $arg[ 1 ] );
  2964. $buf[ 2 ] = 0 if( scalar( @buf ) == 2 ); # CD 0016, von MM übernommen, geändert
  2965. if( scalar( @buf ) != 3 ) {
  2966. my $msg = "SB_PLAYER_Set: please use hh:mm:ss for alarm time.";
  2967. Log3( $hash, 3, $msg );
  2968. return( $msg );
  2969. }
  2970. my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ];
  2971. $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id ";
  2972. $cmdstr .= "time:" . $secs . "\n";
  2973. IOWrite( $hash, $cmdstr );
  2974. # CD 0017 start
  2975. if($updateReadingsOnSet) {
  2976. my $buf = sprintf( "%02d:%02d:%02d",
  2977. int( scalar( $secs ) / 3600 ),
  2978. int( ( $secs % 3600 ) / 60 ),
  2979. int( $secs % 60 ) );
  2980. readingsSingleUpdate( $hash, "alarm".$n."_time", $buf, $donotnotify );
  2981. }
  2982. # CD 0017 end
  2983. }
  2984. # CD 0015 end
  2985. } elsif( $arg[ 0 ] eq "delete" ) {
  2986. if( $id ne "none" ) {
  2987. $cmdstr = "$hash->{PLAYERMAC} alarm delete id:$id\n";
  2988. IOWrite( $hash, $cmdstr );
  2989. # readingsSingleUpdate( $hash, "alarmid$n", "none", 1 ); # CD 0015 deaktiviert
  2990. }
  2991. } else {
  2992. my $msg = "SB_PLAYER_Set: unkown argument ".$arg[ 0 ]." for alarm given.";
  2993. Log3( $hash, 3, $msg );
  2994. return( $msg );
  2995. }
  2996. return( undef );
  2997. }
  2998. # CD 0016, neu, von MM übernommen
  2999. # ----------------------------------------------------------------------------
  3000. # Check weekdays string
  3001. # ----------------------------------------------------------------------------
  3002. sub SB_PLAYER_CheckWeekdays( $ ) {
  3003. my ($wdayargs) = @_;
  3004. my $weekdays = '';
  3005. if(index($wdayargs,"Mo") != -1 || index($wdayargs,"1") != -1)
  3006. {
  3007. $weekdays.='1,';
  3008. }
  3009. if(index($wdayargs,"Tu") != -1 || index($wdayargs,"Di") != -1 || index($wdayargs,"2") != -1)
  3010. {
  3011. $weekdays.='2,';
  3012. }
  3013. if(index($wdayargs,"We") != -1 || index($wdayargs,"Mi") != -1 || index($wdayargs,"3") != -1)
  3014. {
  3015. $weekdays.='3,';
  3016. }
  3017. if(index($wdayargs,"Th") != -1 || index($wdayargs,"Do") != -1 || index($wdayargs,"4") != -1)
  3018. {
  3019. $weekdays.='4,';
  3020. }
  3021. if(index($wdayargs,"Fr") != -1 || index($wdayargs,"5") != -1)
  3022. {
  3023. $weekdays.='5,';
  3024. }
  3025. if(index($wdayargs,"Sa") != -1 || index($wdayargs,"6") != -1)
  3026. {
  3027. $weekdays.='6,';
  3028. }
  3029. if(index($wdayargs,"Su") != -1 || index($wdayargs,"So") != -1 || index($wdayargs,"0") != -1)
  3030. {
  3031. $weekdays.='0';
  3032. }
  3033. if(index($wdayargs,"all") != -1 || index($wdayargs,"daily") != -1 || index($wdayargs,"7") != -1)
  3034. {
  3035. $weekdays='0,1,2,3,4,5,6';
  3036. }
  3037. if(index($wdayargs,"none") != -1) # || index($wdayargs,"once") != -1) # CD 0016 once funktioniert so nicht, muss über repeat gemacht werden
  3038. {
  3039. $weekdays='7';
  3040. }
  3041. $weekdays=~ s/,$//; # CD 0019 letztes , entfernen
  3042. return $weekdays;
  3043. }
  3044. # ----------------------------------------------------------------------------
  3045. # Status update - just internal use and invoked by the timer
  3046. # ----------------------------------------------------------------------------
  3047. sub SB_PLAYER_GetStatus( $ ) {
  3048. my ($hash) = @_;
  3049. my $name = $hash->{NAME};
  3050. my $strbuf = "";
  3051. Log3( $hash, 5, "SB_PLAYER_GetStatus: called" );
  3052. # CD 0014 start - Anzahl Anfragen begrenzen
  3053. if(!defined($hash->{helper}{lastGetStatus})||($hash->{helper}{lastGetStatus}<gettimeofday()-0.5)) {
  3054. #Log 0,"Querying status, last: $hash->{helper}{lastGetStatus}, now: ".gettimeofday();
  3055. $hash->{helper}{lastGetStatus}=gettimeofday();
  3056. # we fire the respective questions and parse the answers in parse
  3057. IOWrite( $hash, "$hash->{PLAYERMAC} artist ?\n" );
  3058. IOWrite( $hash, "$hash->{PLAYERMAC} album ?\n" );
  3059. IOWrite( $hash, "$hash->{PLAYERMAC} title ?\n" );
  3060. IOWrite( $hash, "$hash->{PLAYERMAC} playlist url ?\n" );
  3061. IOWrite( $hash, "$hash->{PLAYERMAC} remote ?\n" );
  3062. IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ); # CD 0030 u added to tags
  3063. #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ) if (!defined($hash->{helper}{alarmPlaylists})); # CD 0016 get available elements for alarms before querying the alarms # CD 0026 wird über Server verteilt
  3064. IOWrite( $hash, "$hash->{PLAYERMAC} alarms 0 200 tags:all filter:all\n" ); # CD 0015 filter added
  3065. # MM 0016 start
  3066. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmsEnabled ?\n" );
  3067. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmDefaultVolume ?\n" );
  3068. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmTimeoutSeconds ?\n" );
  3069. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref alarmSnoozeSeconds ?\n" );
  3070. # MM 0016 end
  3071. # CD 0007
  3072. IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume ?\n" );
  3073. # CD 0009
  3074. IOWrite( $hash, "$hash->{PLAYERMAC} playlist name ?\n" );
  3075. # CD 0048
  3076. IOWrite( $hash, "$hash->{PLAYERMAC} playlist path 0 ?\n" );
  3077. # CD 0014
  3078. IOWrite( $hash, "$hash->{PLAYERMAC} duration ?\n" );
  3079. SB_PLAYER_QueryElapsedTime($hash);
  3080. } # CD 0014 end
  3081. # the other values below are provided by our server. we don't
  3082. # need to ask again
  3083. if( $hash->{PLAYERIP} eq "?" ) {
  3084. # the server doesn't care about us
  3085. IOWrite( $hash, "$hash->{PLAYERMAC} player ip ?\n" );
  3086. }
  3087. if( $hash->{MODEL} eq "?" ) {
  3088. IOWrite( $hash, "$hash->{PLAYERMAC} player model ?\n" );
  3089. }
  3090. if( $hash->{CANPOWEROFF} eq "?" ) {
  3091. IOWrite( $hash, "$hash->{PLAYERMAC} player canpoweroff ?\n" );
  3092. }
  3093. if( $hash->{PLAYERNAME} eq "?" ) {
  3094. IOWrite( $hash, "$hash->{PLAYERMAC} name ?\n" );
  3095. }
  3096. if( ReadingsVal( $name, "state", "?" ) eq "?" ) {
  3097. IOWrite( $hash, "$hash->{PLAYERMAC} power ?\n" );
  3098. }
  3099. if( ReadingsVal( $name, "connected", "?" ) eq "?" ) {
  3100. IOWrite( $hash, "$hash->{PLAYERMAC} connected ?\n" );
  3101. }
  3102. # do and update of the status
  3103. RemoveInternalTimer( $hash ); # CD 0014
  3104. my $iv=AttrVal($name, "statusRequestInterval", 300); # CD 0037
  3105. InternalTimer( gettimeofday() + $iv,
  3106. "SB_PLAYER_GetStatus",
  3107. $hash,
  3108. 0 ) if $iv>0;
  3109. Log3( $hash, 5, "SB_PLAYER_GetStatus: leaving" );
  3110. return( );
  3111. }
  3112. # ----------------------------------------------------------------------------
  3113. # called from the IODev for Broadcastmessages
  3114. # ----------------------------------------------------------------------------
  3115. sub SB_PLAYER_RecBroadcast( $$@ ) {
  3116. my ( $hash, $cmd, $msg, $bin ) = @_;
  3117. my $name = $hash->{NAME};
  3118. Log3( $hash, 5, "SB_PLAYER_Broadcast($name): called with $msg" );
  3119. # let's see what we got. Split the data at the space
  3120. my @args = split( " ", $msg );
  3121. if( $cmd eq "SERVER" ) {
  3122. # a message from the server
  3123. if( $args[ 0 ] eq "OFF" ) {
  3124. # the server is off, so are we
  3125. RemoveInternalTimer( $hash );
  3126. readingsSingleUpdate( $hash, "state", "off", 1 );
  3127. readingsSingleUpdate( $hash, "power", "off", 1 );
  3128. SB_PLAYER_Amplifier( $hash );
  3129. } elsif( $args[ 0 ] eq "ON" ) {
  3130. # the server is back
  3131. #readingsSingleUpdate( $hash, "state", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten
  3132. #readingsSingleUpdate( $hash, "power", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten
  3133. # do and update of the status
  3134. RemoveInternalTimer( $hash ); # CD 0016
  3135. InternalTimer( gettimeofday() + 10,
  3136. "SB_PLAYER_GetStatus",
  3137. $hash,
  3138. 0 );
  3139. } elsif( $args[ 0 ] eq "IP" ) {
  3140. $hash->{SBSERVER} = $args[ 1 ];
  3141. # CD 0043 bei Änderung von Adresse oder Port COVERART aktualisieren
  3142. readingsBeginUpdate( $hash );
  3143. SB_PLAYER_CoverArt( $hash );
  3144. if( AttrVal( $name, "donotnotify", "false" ) eq "true" ) {
  3145. readingsEndUpdate( $hash, 0 );
  3146. } else {
  3147. readingsEndUpdate( $hash, 1 );
  3148. }
  3149. # CD 0043 end
  3150. } else {
  3151. # unkown broadcast message
  3152. }
  3153. } elsif( $cmd eq "FAVORITES" ) {
  3154. if( $args[ 0 ] eq "ADD" ) {
  3155. # format: ADD IODEVname ID shortentry
  3156. $hash->{helper}{SB_PLAYER_Favs}{$args[3]}{ID} = $args[ 2 ];
  3157. $hash->{helper}{SB_PLAYER_Favs}{$args[3]}{URL} = $args[ 4 ]; # CD 0021 hinzugefügt
  3158. if( $hash->{FAVSTR} eq "" ) {
  3159. $hash->{FAVSTR} = $args[ 3 ]; # CD Test für Leerzeichen join("&nbsp;",@args[ 4..$#args ]);
  3160. } else {
  3161. $hash->{FAVSTR} .= "," . $args[ 3 ]; # CD Test für Leerzeichen join("&nbsp;",@args[ 4..$#args ]);
  3162. }
  3163. # CD 0016 start, provisorisch um alarmPlaylists zu aktualisieren, TODO: muss von 97_SB_SERVER kommen
  3164. RemoveInternalTimer( $hash ); # CD 0016
  3165. InternalTimer( gettimeofday() + 3,
  3166. "SB_PLAYER_GetStatus",
  3167. $hash,
  3168. 0 );
  3169. #end
  3170. } elsif( $args[ 0 ] eq "FLUSH" ) {
  3171. undef( %{$hash->{helper}{SB_PLAYER_Favs}} );
  3172. $hash->{FAVSTR} = "";
  3173. delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016
  3174. } else {
  3175. }
  3176. } elsif( $cmd eq "SYNCMASTER" ) {
  3177. if( $args[ 0 ] eq "ADD" ) {
  3178. if(( $args[ 1 ] ne $hash->{PLAYERNAME} ) || ( $args[ 2 ] ne $hash->{PLAYERMAC} )) { # CD 0056 Name oder MAC müssen unterschiedlich sein
  3179. # CD 0056 mehrere Player haben gleichen Namen -> Namen anpassen
  3180. if(( $args[ 1 ] eq $hash->{PLAYERNAME} ) || (defined($hash->{helper}{SB_PLAYER_SyncMasters}{$args[1]}))) {
  3181. $args[ 1 ]=$args[ 1 ] . "_MAC_" . $args[ 3 ];
  3182. }
  3183. # CD 0056 Ende
  3184. $hash->{helper}{SB_PLAYER_SyncMasters}{$args[1]}{MAC} = $args[ 2 ];
  3185. if( $hash->{SYNCMASTERS} eq "" ) {
  3186. $hash->{SYNCMASTERS} = $args[ 1 ];
  3187. } else {
  3188. $hash->{SYNCMASTERS} .= "," . $args[ 1 ];
  3189. }
  3190. }
  3191. } elsif( $args[ 0 ] eq "FLUSH" ) {
  3192. undef( %{$hash->{helper}{SB_PLAYER_SyncMasters}} );
  3193. $hash->{SYNCMASTERS} = "";
  3194. } else {
  3195. }
  3196. } elsif( $cmd eq "PLAYLISTS" ) {
  3197. if( $args[ 0 ] eq "ADD" ) {
  3198. # CD 0014 Playlists mit fhem_* ignorieren
  3199. if($args[3]=~/^fhem_.*/) {
  3200. # CD 0036 start
  3201. if($args[3]=~/$name/) {
  3202. my $plname=$args[1];
  3203. # kein Name ?
  3204. if($args[1]=~/^fhem_$name$/) {
  3205. $plname='default';
  3206. } else {
  3207. $plname=~s/^fhem_$name//;
  3208. $plname=~s/^_//;
  3209. }
  3210. Log3( $hash, 4, "SB_PLAYER_RecbroadCast($name): - adding - PLAYLISTS ADD " .
  3211. "name:$args[1] id:$args[2] uid:$plname" );
  3212. $hash->{helper}{myPlaylists}{$plname}{ID} = $args[ 2 ];
  3213. $hash->{helper}{myPlaylists}{$plname}{NAME} = $args[ 1 ];
  3214. } else {
  3215. # CD 0036 end
  3216. Log3( $hash, 5, "SB_PLAYER_RecbroadCast($name): - ignoring - PLAYLISTS ADD " .
  3217. "name:$args[1] id:$args[2] uid:$args[3]" );
  3218. }
  3219. } else {
  3220. # CD 0014 end
  3221. Log3( $hash, 5, "SB_PLAYER_RecbroadCast($name): PLAYLISTS ADD " .
  3222. "name:$args[1] id:$args[2] uid:$args[3]" );
  3223. $hash->{helper}{SB_PLAYER_Playlists}{$args[3]}{ID} = $args[ 2 ];
  3224. $hash->{helper}{SB_PLAYER_Playlists}{$args[3]}{NAME} = $args[ 1 ];
  3225. if( $hash->{SERVERPLAYLISTS} eq "" ) {
  3226. $hash->{SERVERPLAYLISTS} = $args[ 3 ];
  3227. } else {
  3228. $hash->{SERVERPLAYLISTS} .= "," . $args[ 3 ];
  3229. }
  3230. } # CD 0014
  3231. # CD 0016 start, provisorisch um alarmPlaylists zu aktualisieren, TODO: muss von 97_SB_SERVER kommen
  3232. RemoveInternalTimer( $hash ); # CD 0016
  3233. InternalTimer( gettimeofday() + 3,
  3234. "SB_PLAYER_GetStatus",
  3235. $hash,
  3236. 0 );
  3237. #end
  3238. } elsif( $args[ 0 ] eq "FLUSH" ) {
  3239. undef( %{$hash->{helper}{SB_PLAYER_Playlists}} );
  3240. undef( %{$hash->{helper}{myPlaylists}} ); # CD 0036
  3241. $hash->{SERVERPLAYLISTS} = "";
  3242. delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016
  3243. } else {
  3244. }
  3245. # CD 0026 start
  3246. } elsif( $cmd eq "ALARMPLAYLISTS" ) {
  3247. if( $args[ 0 ] eq "ADD" ) {
  3248. $hash->{helper}{alarmPlaylists}{$args[ 1 ]}{$args[ 2 ]}=join( " ", @args[3..$#args]);
  3249. } elsif( $args[ 0 ] eq "FLUSH" ) {
  3250. delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists}));
  3251. }
  3252. # CD 0026 end
  3253. } else {
  3254. }
  3255. }
  3256. # ----------------------------------------------------------------------------
  3257. # parse the return on the alarms status
  3258. # ----------------------------------------------------------------------------
  3259. # wird von SB_PLAYER_Parse aufgerufen, readingsBeginUpdate ist aktiv
  3260. sub SB_PLAYER_ParseAlarms( $@ ) {
  3261. my ( $hash, @data ) = @_;
  3262. my $name = $hash->{NAME};
  3263. # CD 0016 start {ALARMSCOUNT} verschieben nach reload
  3264. if (defined($hash->{ALARMSCOUNT})) {
  3265. $hash->{helper}{ALARMSCOUNT}=$hash->{ALARMSCOUNT};
  3266. delete($hash->{ALARMSCOUNT});
  3267. }
  3268. # CD 0016
  3269. my $lastAlarmCount=$hash->{helper}{ALARMSCOUNT}; # CD 0016 ALARMSCOUNT nach {helper} verschoben
  3270. if( $data[ 0 ] =~ /^([0-9])*/ ) {
  3271. shift( @data );
  3272. }
  3273. if( $data[ 0 ] =~ /^([0-9])*/ ) {
  3274. shift( @data );
  3275. }
  3276. fhem( "deletereading $name alarmid.*" ); # CD 0015 alte readings entfernen
  3277. my $alarmcounter=0; # CD 0015
  3278. foreach( @data ) {
  3279. if( $_ =~ /^(id:)(\S{8})/ ) {
  3280. # id is 8 non-white-space characters
  3281. # example: id:0ac7f3a2
  3282. $alarmcounter+=1; # CD 0015
  3283. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_id", $2 ); # CD 0015
  3284. next;
  3285. } elsif( $_ =~ /^(dow:)([0-9,]*)/ ) { # C 0016 + durch * ersetzt, für dow: ohne Tage
  3286. # example: dow:1,2,4,5,6
  3287. my $rdaystr=""; # CD 0015
  3288. if ($2 ne "") { # CD 0016
  3289. if( index( $2, "1" ) != -1 ) {
  3290. $rdaystr = "Mo"; # CD 0015
  3291. }
  3292. if( index( $2, "2" ) != -1 ) {
  3293. $rdaystr .= " Tu"; # CD 0015
  3294. }
  3295. if( index( $2, "3" ) != -1 ) {
  3296. $rdaystr .= " We"; # CD 0015
  3297. }
  3298. if( index( $2, "4" ) != -1 ) {
  3299. $rdaystr .= " Th"; # CD 0015
  3300. }
  3301. if( index( $2, "5" ) != -1 ) {
  3302. $rdaystr .= " Fr"; # CD 0015
  3303. }
  3304. if( index( $2, "6" ) != -1 ) {
  3305. $rdaystr .= " Sa"; # CD 0015
  3306. }
  3307. if( index( $2, "0" ) != -1 ) {
  3308. $rdaystr .= " Su"; # CD 0015
  3309. }
  3310. } else { # CD 0016
  3311. $rdaystr = "none"; # CD 0016
  3312. } # CD 0016
  3313. $rdaystr =~ s/^\s+|\s+$//g; # CD 0015
  3314. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_wdays", $rdaystr ); # CD 0015
  3315. next;
  3316. } elsif( $_ =~ /^(enabled:)([0|1])/ ) {
  3317. # example: enabled:1
  3318. if( $2 eq "1" ) {
  3319. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_state", "on" ); # CD 0015
  3320. } else {
  3321. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_state", "off" ); # CD 0015
  3322. }
  3323. next;
  3324. } elsif( $_ =~ /^(repeat:)([0|1])/ ) {
  3325. # example: repeat:1
  3326. if( $2 eq "1" ) {
  3327. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_repeat", "yes" ); # CD 0015
  3328. } else {
  3329. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_repeat", "no" ); # CD 0015
  3330. }
  3331. next;
  3332. } elsif( $_ =~ /^(time:)([0-9]+)/ ) {
  3333. # example: time:25200
  3334. my $buf = sprintf( "%02d:%02d:%02d",
  3335. int( scalar( $2 ) / 3600 ),
  3336. int( ( $2 % 3600 ) / 60 ),
  3337. int( $2 % 60 ) );
  3338. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_time", $buf ); # CD 0015
  3339. next;
  3340. } elsif( $_ =~ /^(volume:)(\d{1,2})/ ) {
  3341. # example: volume:50
  3342. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_volume", $2 ); # CD 0015
  3343. next;
  3344. } elsif( $_ =~ /^(url:)(\S+)/ ) {
  3345. # CD 0015 start
  3346. my $pn=SB_SERVER_FavoritesName2UID(uri_unescape($2));
  3347. if(defined($hash->{helper}{alarmPlaylists})
  3348. && defined($hash->{helper}{alarmPlaylists}{$pn})) {
  3349. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_sound", $hash->{helper}{alarmPlaylists}{$pn}{title} );
  3350. } else {
  3351. readingsBulkUpdate( $hash, "alarm".$alarmcounter."_sound", $2 );
  3352. }
  3353. # CD 0015 end
  3354. next;
  3355. # CD 0016 start
  3356. } elsif( $_ =~ /^(filter:)(\S+)/ ) {
  3357. next;
  3358. # CD 0016 end
  3359. # MM 0016 start
  3360. } elsif( $_ =~ /^(tags:)(\S+)/ ) {
  3361. next;
  3362. } elsif( $_ =~ /^(fade:)([0|1])/ ) {
  3363. # example: fade:1
  3364. if( $2 eq "1" ) {
  3365. readingsBulkUpdate( $hash, "alarmsFadeIn", "on" ); # CD 0016 von MM übernommen, Namen geändert
  3366. } else {
  3367. readingsBulkUpdate( $hash, "alarmsFadeIn", "off" ); # CD 0016 von MM übernommen, Namen geändert
  3368. }
  3369. next;
  3370. } elsif( $_ =~ /^(count:)([0-9]+)/ ) {
  3371. $hash->{helper}{ALARMSCOUNT} = $2; # CD 0016 ALARMSCOUNT nach {helper} verschoben
  3372. next;
  3373. } else {
  3374. Log3( $hash, 1, "SB_PLAYER_Alarms($name): Unknown data ($_)");
  3375. next;
  3376. }
  3377. # MM 0016 end
  3378. }
  3379. # CD 0015 nicht mehr vorhandene Alarme löschen
  3380. if ($lastAlarmCount>$hash->{helper}{ALARMSCOUNT}) { # CD 0016 ALARMSCOUNT nach {helper} verschoben
  3381. for(my $i=$hash->{helper}{ALARMSCOUNT}+1;$i<=$lastAlarmCount;$i++) { # CD 0016 ALARMSCOUNT nach {helper} verschoben
  3382. fhem( "deletereading $name alarm".$i."_.*" );
  3383. }
  3384. }
  3385. }
  3386. # CD 0046 start
  3387. # ----------------------------------------------------------------------------
  3388. # search for LMS alarm id, return FHEM alarm number
  3389. # ----------------------------------------------------------------------------
  3390. sub SB_PLAYER_FindAlarmId( $$ ) {
  3391. my ( $hash, $id ) = @_;
  3392. my $name = $hash->{NAME};
  3393. my $n=0;
  3394. for (my $i=0;$i<=$hash->{helper}{ALARMSCOUNT};$i++) {
  3395. if (ReadingsVal($name,"alarm".$i."_id","xxxx") eq $id) {
  3396. $n=$i;
  3397. last;
  3398. }
  3399. }
  3400. return $n;
  3401. }
  3402. # CD 0046
  3403. # ----------------------------------------------------------------------------
  3404. # used for checking, if the string contains a valid MAC adress
  3405. # ----------------------------------------------------------------------------
  3406. sub SB_PLAYER_IsValidMAC( $ ) {
  3407. my $instr = shift( @_ );
  3408. my $d = "[0-9A-Fa-f]";
  3409. my $dd = "$d$d";
  3410. if( $instr =~ /($dd([:-])$dd(\2$dd){4})/og ) {
  3411. if( $instr =~ /^(00[:-]){5}(00)$/) { # CD 0032 00:00:00:00:00:00 is not a valid MAC
  3412. return( 0 );
  3413. } else {
  3414. return( 1 );
  3415. }
  3416. } else {
  3417. return( 0 );
  3418. }
  3419. }
  3420. # ----------------------------------------------------------------------------
  3421. # used to turn on our server
  3422. # ----------------------------------------------------------------------------
  3423. sub SB_PLAYER_ServerTurnOn( $ ) {
  3424. my ( $hash ) = @_;
  3425. my $name = $hash->{NAME};
  3426. my $servername;
  3427. Log3( $hash, 5, "SB_PLAYER_ServerTurnOn($name): please implement me" );
  3428. return;
  3429. fhem( "set $servername on" );
  3430. }
  3431. # ----------------------------------------------------------------------------
  3432. # used to turn on a connected amplifier
  3433. # ----------------------------------------------------------------------------
  3434. # CD 0012 start
  3435. sub SB_PLAYER_tcb_DelayAmplifier( $ ) { # CD 0014 Name geändert
  3436. my($in ) = shift;
  3437. my(undef,$name) = split(':',$in);
  3438. my $hash = $defs{$name};
  3439. #Log 0,"SB_PLAYER_DelayAmplifier";
  3440. $hash->{helper}{AMPLIFIERDELAYOFF}=1;
  3441. SB_PLAYER_Amplifier($hash);
  3442. }
  3443. # CD 0012 end
  3444. sub SB_PLAYER_Amplifier( $ ) {
  3445. my ( $hash ) = @_;
  3446. my $name = $hash->{NAME};
  3447. if( ( $hash->{AMPLIFIER} eq "none" ) ||
  3448. ( !defined( $defs{$hash->{AMPLIFIER}} ) ) ) {
  3449. # amplifier not specified
  3450. delete($hash->{helper}{AMPLIFIERDELAYOFF}) if defined($hash->{helper}{AMPLIFIERDELAYOFF}); # CD 0012
  3451. return;
  3452. }
  3453. my $setvalue = "off";
  3454. my $delayAmp=0.01; # CD 0043
  3455. Log3( $hash, 4, "SB_PLAYER_Amplifier($name): called" );
  3456. if( AttrVal( $name, "amplifier", "play" ) eq "play" ) {
  3457. my $thestatus = ReadingsVal( $name, "playStatus", "pause" );
  3458. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode play " .
  3459. "and status:$thestatus" );
  3460. if( ( $thestatus eq "playing" ) || (( $thestatus eq "paused" ) && ($hash->{helper}{amplifierDelayOffPause}==0)) ) { # CD 0043 DelayOffPause abfragen
  3461. $setvalue = "on";
  3462. # CD 0043 start
  3463. } elsif(( $thestatus eq "paused" ) && ($hash->{helper}{amplifierDelayOffPause}>0)) {
  3464. $setvalue = "off";
  3465. $delayAmp=$hash->{helper}{amplifierDelayOffPause};
  3466. # CD 0043 end
  3467. } elsif( $thestatus eq "stopped" ) {
  3468. $setvalue = "off";
  3469. $delayAmp=$hash->{helper}{amplifierDelayOffStop}; # CD 0043
  3470. } else {
  3471. $setvalue = "off";
  3472. }
  3473. } elsif( AttrVal( $name, "amplifier", "on" ) eq "on" ) {
  3474. my $thestatus = ReadingsVal( $name, "power", "off" );
  3475. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode on " .
  3476. "and status:$thestatus" );
  3477. if( $thestatus eq "on" ) {
  3478. $setvalue = "on";
  3479. } else {
  3480. $setvalue = "off";
  3481. $delayAmp=$hash->{helper}{amplifierDelayOffPower}; # CD 0043
  3482. }
  3483. } else {
  3484. Log3( $hash, 1, "SB_PLAYER_Amplifier($name): ATTR amplifier " .
  3485. "set to wrong value [on|play]" );
  3486. return;
  3487. }
  3488. my $actualState = ReadingsVal( "$hash->{AMPLIFIER}", "state", "off" );
  3489. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): actual:$actualState " .
  3490. "and set:$setvalue" );
  3491. if ( $actualState ne $setvalue) {
  3492. # CD 0012 start - Abschalten über Attribut verzögern, generell verzögern damit set-Event funktioniert
  3493. # my $delayAmp=($setvalue eq "off")?AttrVal( $name, "amplifierDelayOff", 0 ):0.1; # CD 0043 deaktiviert, wird oben behandelt
  3494. $delayAmp=0.01 if($delayAmp==0);
  3495. if (!defined($hash->{helper}{AMPLIFIERDELAYOFF})) {
  3496. # CD 0043 Timer nicht neu starten wenn Zustand sich nicht geändert hat
  3497. if ((!defined($hash->{helper}{AMPLIFIERACTIVETIMER})) || ($hash->{helper}{AMPLIFIERACTIVETIMER} ne ($actualState.$setvalue))) {
  3498. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): delaying amplifier on/off by $delayAmp" );
  3499. RemoveInternalTimer( "DelayAmplifier:$name");
  3500. InternalTimer( gettimeofday() + $delayAmp,
  3501. "SB_PLAYER_tcb_DelayAmplifier", # CD 0014 Name geändert
  3502. "DelayAmplifier:$name",
  3503. 0 );
  3504. $hash->{helper}{AMPLIFIERACTIVETIMER}=$actualState.$setvalue; # CD 0043
  3505. } else {
  3506. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): delay already active" );
  3507. }
  3508. return;
  3509. }
  3510. # CD 0012 end
  3511. fhem( "set $hash->{AMPLIFIER} $setvalue" );
  3512. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): amplifier changed to " .
  3513. $setvalue );
  3514. } else {
  3515. Log3( $hash, 5, "SB_PLAYER_Amplifier($name): no amplifier " .
  3516. "state change" );
  3517. }
  3518. delete($hash->{helper}{AMPLIFIERDELAYOFF}) if (defined($hash->{helper}{AMPLIFIERDELAYOFF}));
  3519. delete($hash->{helper}{AMPLIFIERACTIVETIMER}) if (defined($hash->{helper}{AMPLIFIERACTIVETIMER})); # CD 0043
  3520. return;
  3521. }
  3522. # ----------------------------------------------------------------------------
  3523. # update the coverart image
  3524. # ----------------------------------------------------------------------------
  3525. sub SB_PLAYER_CoverArt( $ ) {
  3526. my ( $hash ) = @_;
  3527. my $name = $hash->{NAME};
  3528. # return if (defined($hash->{helper}{CoverOk}) && ($hash->{helper}{CoverOk} == 1) && ( $hash->{ISREMOTESTREAM} eq "0" )); # CD 0026 added # CD 0027 removed
  3529. # CD 0003 fix missing server
  3530. if(!defined($hash->{SBSERVER})||($hash->{SBSERVER} eq '?')) {
  3531. if ((defined($hash->{IODev})) && (defined($hash->{IODev}->{IP}))) {
  3532. $hash->{SBSERVER}=$hash->{IODev}->{IP} . ":" . AttrVal( $hash->{IODev}, "httpport", "9000" );
  3533. }
  3534. }
  3535. # CD 0003 end
  3536. my $lastCoverartUrl=$hash->{COVERARTURL}; # CD 0013
  3537. # compile the link to the album cover
  3538. if(( $hash->{ISREMOTESTREAM} eq "0" ) || ($hash->{ARTWORKURL} =~ /imageproxy%2F/)) { # CD 0026 LMS 7.8/7.9
  3539. $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" .
  3540. "current/cover_" . AttrVal( $name, "coverartheight", 50 ) .
  3541. "x" . AttrVal( $name, "coverartwidth", 50 ) .
  3542. ".jpg?player=$hash->{PLAYERMAC}&x=".int(rand(100000)); # CD 0025 added rand() to force browser refresh
  3543. $hash->{helper}{CoverOk}=1; # CD 0026 added
  3544. } elsif( $hash->{ISREMOTESTREAM} eq "1" ) { # CD 0017 Abfrage || ( $hash->{ISREMOTESTREAM} == 1 ) entfernt
  3545. # CD 0011 überprüfen ob überhaupt eine URL vorhanden ist
  3546. if($hash->{ARTWORKURL} ne "?") {
  3547. # CD 0034 Abfrage für Spotify und LMS < 7.8, ungetest, #674, KernSani
  3548. # CD 0035 Code von KernSani übernommen, #676
  3549. if ($hash->{ARTWORKURL} =~ /spotifyimage%2Fspotify/) {
  3550. my $cover = "cover.jpg";
  3551. my $coverArtWithSize = "cover_".AttrVal( $name, "coverartheight", 50 )."x".AttrVal( $name, "coverartwidth", 50 )."_o.jpg";
  3552. $hash->{ARTWORKURL} =~ s/$cover/$coverArtWithSize/g;
  3553. $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/" . uri_unescape($hash->{ARTWORKURL});
  3554. } else {
  3555. $hash->{COVERARTURL} = "http://www.mysqueezebox.com/public/" .
  3556. "imageproxy?u=" . $hash->{ARTWORKURL} .
  3557. "&h=" . AttrVal( $name, "coverartheight", 50 ) .
  3558. "&w=". AttrVal( $name, "coverartwidth", 50 );
  3559. }
  3560. } else {
  3561. $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" .
  3562. $hash->{COVERID} . "/cover_" . AttrVal( $name, "coverartheight", 50 ) .
  3563. "x" . AttrVal( $name, "coverartwidth", 50 ) . ".jpg";
  3564. }
  3565. # CD 0011 Ende
  3566. } else {
  3567. $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" .
  3568. $hash->{COVERID} . "/cover_" . AttrVal( $name, "coverartheight", 50 ) . # CD 0011 -160206228 durch $hash->{COVERID} ersetzt
  3569. "x" . AttrVal( $name, "coverartwidth", 50 ) . ".jpg";
  3570. }
  3571. # CD 0004, url as reading
  3572. readingsBulkUpdate( $hash, "coverarturl", $hash->{COVERARTURL});
  3573. if( ( $hash->{COVERARTLINK} eq "none" ) ||
  3574. ( !defined( $defs{$hash->{COVERARTLINK}} ) ) ||
  3575. ( $hash->{COVERARTURL} eq "?" ) ) {
  3576. # weblink not specified
  3577. return;
  3578. } else {
  3579. if ($lastCoverartUrl ne $hash->{COVERARTURL}) { # CD 0013 nur bei Änderung aktualisieren
  3580. fhem( "modify " . $hash->{COVERARTLINK} . " image " .
  3581. $hash->{COVERARTURL} );
  3582. } # CD 0013
  3583. }
  3584. }
  3585. # ----------------------------------------------------------------------------
  3586. # Handle the return for a playerstatus query
  3587. # ----------------------------------------------------------------------------
  3588. sub SB_PLAYER_ParsePlayerStatus( $$ ) {
  3589. my( $hash, $dataptr ) = @_;
  3590. my $name = $hash->{NAME};
  3591. my $leftover = "";
  3592. my $cur = "";
  3593. my $playlistIds = ""; # CD 0014
  3594. my $refreshIds=0; # CD 0014
  3595. # typically the start index being a number
  3596. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  3597. shift( @{$dataptr} );
  3598. } else {
  3599. Log3( $hash, 5, "SB_PLAYER_ParsePlayerStatus($name): entry is " .
  3600. "not the start number" );
  3601. return;
  3602. }
  3603. # typically the max index being a number
  3604. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  3605. if($dataptr->[ 0 ]>1) {
  3606. $refreshIds=1; # CD 0014
  3607. delete($hash->{helper}{playlistUrls}) if(defined($hash->{helper}{playlistUrls})); # CD 0030
  3608. }
  3609. shift( @{$dataptr} );
  3610. } else {
  3611. Log3( $hash, 5, "SB_PLAYER_ParsePlayerStatus($name): entry is " .
  3612. "not the end number" );
  3613. return;
  3614. }
  3615. my $datastr = join( " ", @{$dataptr} );
  3616. # replace funny stuff
  3617. # CD 0006 all keywords with spaces must be converted here
  3618. $datastr =~ s/mixer volume/mixervolume/g;
  3619. # CD 0006 replaced mixertreble with mixer treble
  3620. $datastr =~ s/mixer treble/mixertreble/g;
  3621. $datastr =~ s/mixer bass/mixerbass/g;
  3622. $datastr =~ s/mixer pitch/mixerpitch/g;
  3623. $datastr =~ s/playlist repeat/playlistrepeat/g;
  3624. $datastr =~ s/playlist shuffle/playlistshuffle/g;
  3625. $datastr =~ s/playlist index/playlistindex/g;
  3626. # CD 0003
  3627. $datastr =~ s/playlist mode/playlistmode/g;
  3628. Log3( $hash, 5, "SB_PLAYER_ParsePlayerStatus($name): data to parse: " .
  3629. $datastr );
  3630. my @data1 = split( " ", $datastr );
  3631. # the rest of the array should now have the data, we're interested in
  3632. # CD 0006 - deaktiviert, SB_PLAYER_ParsePlayerStatus kann nur von SB_PLAYER_Parse aufgerufen werden, dort ist readingsBeginUpdate aber bereits aktiv
  3633. #readingsBeginUpdate( $hash );
  3634. # set default values for stuff not always send
  3635. my $oldsyncmaster=$hash->{SYNCMASTER}; # CD 0056
  3636. my $oldsyncgroup=$hash->{SYNCGROUP}; # CD 0056
  3637. $hash->{SYNCMASTER} = "none";
  3638. $hash->{SYNCGROUP} = "none";
  3639. $hash->{SYNCMASTERPN} = "none"; # CD 0018
  3640. $hash->{SYNCGROUPPN} = "none"; # CD 0018
  3641. $hash->{SYNCED} = "no";
  3642. $hash->{COVERID} = "?";
  3643. $hash->{ARTWORKURL} = "?";
  3644. $hash->{ISREMOTESTREAM} = "0";
  3645. # needed for scanning the MAC Adress
  3646. my $d = "[0-9A-Fa-f]";
  3647. my $dd = "$d$d";
  3648. # needed for scanning the IP adress
  3649. my $e = "[0-9]";
  3650. my $ee = "$e$e";
  3651. # CD 0003 start, fix handling of spaces
  3652. my @data2;
  3653. my $last_d="";
  3654. # loop through the results
  3655. foreach( @data1 ) {
  3656. if( index( $_, ":" ) < 2 ) {
  3657. $last_d = $last_d . " " . $_;
  3658. next;
  3659. }
  3660. if( $last_d ne "" ) {
  3661. push @data2,$last_d;
  3662. }
  3663. $last_d=$_;
  3664. }
  3665. if( $last_d ne "" ) {
  3666. push @data2,$last_d;
  3667. }
  3668. # CD 0003 end
  3669. my $lastId=0; # CD 0030
  3670. # loop through the results
  3671. foreach( @data2 ) {
  3672. my $cur=$_;
  3673. if( $cur =~ /^(player_connected:)([0-9]*)/ ) {
  3674. if( $2 == "1" ) {
  3675. readingsBulkUpdate( $hash, "connected", $2 );
  3676. readingsBulkUpdate( $hash, "presence", "present" );
  3677. } else {
  3678. readingsBulkUpdate( $hash, "connected", $3 );
  3679. readingsBulkUpdate( $hash, "presence", "absent" );
  3680. }
  3681. next;
  3682. } elsif( $cur =~ /^(player_ip:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{3,5})/ ) {
  3683. if( $hash->{PLAYERIP} ne "?" ) {
  3684. $hash->{PLAYERIP} = $2;
  3685. }
  3686. next;
  3687. } elsif( $cur =~ /^(player_name:)(.*)/ ) {
  3688. if( $hash->{PLAYERNAME} ne "?" ) {
  3689. $hash->{PLAYERNAME} = $2;
  3690. }
  3691. next;
  3692. } elsif( $cur =~ /^(power:)([0-9\.]*)/ ) {
  3693. if( $2 eq "1" ) {
  3694. readingsBulkUpdate( $hash, "state", "on" ); # CD 0041 hinzugefügt
  3695. readingsBulkUpdate( $hash, "power", "on" );
  3696. SB_PLAYER_Amplifier( $hash );
  3697. # CD 0042 start
  3698. if($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON) {
  3699. RemoveInternalTimer( "TTSStartAfterPowerOn:$name");
  3700. InternalTimer( gettimeofday() + 0.01,
  3701. "SB_PLAYER_tcb_TTSStartAfterPowerOn",
  3702. "TTSStartAfterPowerOn:$name",
  3703. 0 );
  3704. }
  3705. # CD 0042 end
  3706. } else {
  3707. # CD 0042 start
  3708. if(($hash->{helper}{playerStatusOK}==0) && ($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON)) {
  3709. $hash->{helper}{ttsPowerWasOff}=1;
  3710. IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" );
  3711. }
  3712. # CD 0042 end
  3713. readingsBulkUpdate( $hash, "state", "off" ); # CD 0041 hinzugefügt
  3714. readingsBulkUpdate( $hash, "power", "off" );
  3715. SB_PLAYER_Amplifier( $hash );
  3716. }
  3717. next;
  3718. } elsif( $cur =~ /^(signalstrength:)([0-9\.]*)/ ) {
  3719. if( $2 eq "0" ) {
  3720. readingsBulkUpdate( $hash, "signalstrength", "wired" );
  3721. } else {
  3722. readingsBulkUpdate( $hash, "signalstrength", "$2" );
  3723. }
  3724. next;
  3725. } elsif( $cur =~ /^(mode:)(.*)/ ) {
  3726. if( $2 eq "play" ) {
  3727. readingsBulkUpdate( $hash, "playStatus", "playing" );
  3728. SB_PLAYER_Amplifier( $hash );
  3729. } elsif( $2 eq "stop" ) {
  3730. readingsBulkUpdate( $hash, "playStatus", "stopped" );
  3731. SB_PLAYER_Amplifier( $hash );
  3732. } elsif( $2 eq "pause" ) {
  3733. readingsBulkUpdate( $hash, "playStatus", "paused" );
  3734. SB_PLAYER_Amplifier( $hash );
  3735. } else {
  3736. # unkown
  3737. }
  3738. next;
  3739. } elsif( $cur =~ /^(sync_master:)($dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd)/ ) {
  3740. # CD 0056 überprüfen ob Syncmaster gewechselt hat (passiert z.B. bei stop->play wenn aktueller Syncmaster aus ist)
  3741. my $sm=$2;
  3742. if ($oldsyncmaster ne "none") {
  3743. if ($oldsyncmaster ne $2) {
  3744. Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_ParsePlayerStatus: $name: Syncmaster changed from $oldsyncmaster to $sm" );
  3745. if (defined($oldsyncgroup) && ($oldsyncgroup ne '?') && ($oldsyncmaster ne 'none')) {
  3746. my @pl=split(",",$oldsyncgroup.",".$oldsyncmaster);
  3747. foreach (@pl) {
  3748. if ($hash->{PLAYERMAC} ne $_) {
  3749. IOWrite( $hash, "$_ status 0 500 tags:Kcu\n" );
  3750. }
  3751. }
  3752. }
  3753. }
  3754. }
  3755. # CD 0056 end
  3756. $hash->{SYNCMASTER} = $sm;
  3757. $hash->{SYNCED} = "yes";
  3758. $hash->{SYNCMASTERPN} = SB_PLAYER_MACToLMSPlayername($hash,$sm); # CD 0018
  3759. next;
  3760. } elsif( $cur =~ /^(sync_slaves:)(.*)/ ) {
  3761. $hash->{SYNCGROUP} = $2;
  3762. # CD 0018 start
  3763. my @macs=split(",",$hash->{SYNCGROUP});
  3764. my $syncgroup;
  3765. foreach ( @macs ) {
  3766. my $mac=$_;
  3767. my $dev=SB_PLAYER_MACToLMSPlayername($hash,$mac);
  3768. $syncgroup.="," if(defined($syncgroup));
  3769. if(defined($dev)) {
  3770. $syncgroup.=$dev;
  3771. } else {
  3772. if($mac eq $hash->{PLAYERMAC}) {
  3773. $syncgroup.=$hash->{PLAYERNAME}; # CD 0055 FIX: PLAYERNAME verwenden
  3774. } else {
  3775. $syncgroup.=$mac;
  3776. }
  3777. }
  3778. }
  3779. $hash->{SYNCGROUPPN} = $syncgroup;
  3780. # CD 0018 end
  3781. # CD 0055 start
  3782. if(AttrVal($name, "syncedNamesSource", "LMS") eq "FHEM") {
  3783. my @pn=split ',',"$hash->{SYNCMASTERPN},$hash->{SYNCGROUPPN}";
  3784. my @players=devspec2array("TYPE=SB_PLAYER");
  3785. $syncgroup=undef;
  3786. foreach(@players) {
  3787. my $pn=$_;
  3788. my $phash=$defs{$_};
  3789. if($phash->{PLAYERNAME} ~~ @pn) {
  3790. $syncgroup.="," if(defined($syncgroup));
  3791. $syncgroup.=$pn;
  3792. }
  3793. }
  3794. readingsBulkUpdate( $hash, "synced", $syncgroup );
  3795. } else {
  3796. # CD 0055 end
  3797. readingsBulkUpdate( $hash, "synced", "$hash->{SYNCMASTERPN},$hash->{SYNCGROUPPN}" ); # Matthew 0019 hinzugefügt
  3798. }
  3799. next;
  3800. } elsif( $cur =~ /^(will_sleep_in:)([0-9\.]*)/ ) {
  3801. $hash->{WILLSLEEPIN} = "$2 secs";
  3802. next;
  3803. } elsif( $cur =~ /^(mixervolume:)(.*)/ ) {
  3804. if( ( index( $2, "+" ) != -1 ) || ( index( $2, "-" ) != -1 ) ) {
  3805. # that was a relative value. We do nothing and fire an update
  3806. IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ?\n" );
  3807. } else {
  3808. SB_PLAYER_UpdateVolumeReadings( $hash, $2, true );
  3809. # CD 0007 start
  3810. if((defined($hash->{helper}{setSyncVolume}) && ($hash->{helper}{setSyncVolume} != $2))|| (!defined($hash->{helper}{setSyncVolume}))) {
  3811. SB_PLAYER_SetSyncedVolume($hash,$2);
  3812. }
  3813. delete $hash->{helper}{setSyncVolume};
  3814. # CD 0007 end
  3815. }
  3816. next;
  3817. } elsif( $cur =~ /^(playlistshuffle:)(.*)/ ) {
  3818. if( $2 eq "0" ) {
  3819. readingsBulkUpdate( $hash, "shuffle", "off" );
  3820. } elsif( $2 eq "1") {
  3821. readingsBulkUpdate( $hash, "shuffle", "song" );
  3822. } elsif( $2 eq "2") {
  3823. readingsBulkUpdate( $hash, "shuffle", "album" );
  3824. } else {
  3825. readingsBulkUpdate( $hash, "shuffle", "?" );
  3826. }
  3827. next;
  3828. } elsif( $cur =~ /^(playlistrepeat:)(.*)/ ) {
  3829. if( $2 eq "0" ) {
  3830. readingsBulkUpdate( $hash, "repeat", "off" );
  3831. } elsif( $2 eq "1") {
  3832. readingsBulkUpdate( $hash, "repeat", "one" );
  3833. } elsif( $2 eq "2") {
  3834. readingsBulkUpdate( $hash, "repeat", "all" );
  3835. } else {
  3836. readingsBulkUpdate( $hash, "repeat", "?" );
  3837. }
  3838. next;
  3839. } elsif( $cur =~ /^(playlistname:)(.*)/ ) {
  3840. readingsBulkUpdate( $hash, "currentPlaylistName", $2 );
  3841. next;
  3842. } elsif( $cur =~ /^(artwork_url:)(.*)/ ) {
  3843. $hash->{ARTWORKURL} = uri_escape( $2 );
  3844. #Log 0,"Update Artwork: ".$hash->{ARTWORKURL};
  3845. #SB_PLAYER_CoverArt( $hash );
  3846. next;
  3847. } elsif( $cur =~ /^(coverid:)(.*)/ ) {
  3848. $hash->{COVERID} = $2;
  3849. next;
  3850. } elsif( $cur =~ /^(remote:)(.*)/ ) {
  3851. $hash->{ISREMOTESTREAM} = $2;
  3852. next;
  3853. # CD 0014 start
  3854. } elsif( $cur =~ /^(duration:)(.*)/ ) {
  3855. readingsBulkUpdate( $hash, "duration", $2 );
  3856. next;
  3857. } elsif( $cur =~ /^(time:)(.*)/ ) {
  3858. $hash->{helper}{elapsedTime}{VAL}=$2;
  3859. $hash->{helper}{elapsedTime}{TS}=gettimeofday();
  3860. delete($hash->{helper}{saveLocked}) if (($hash->{helper}{ttsstate}==TTS_IDLE) && defined($hash->{helper}{saveLocked}));
  3861. readingsBulkUpdate( $hash, "currentTrackPosition", int($2+0.5) ); # CD 0047
  3862. next;
  3863. } elsif( $cur =~ /^(playlist_tracks:)(.*)/ ) {
  3864. readingsBulkUpdate( $hash, "playlistTracks", $2 );
  3865. next;
  3866. } elsif( $cur =~ /^(playlist_cur_index:)(.*)/ ) {
  3867. readingsBulkUpdate( $hash, "playlistCurrentTrack", $2+1 );
  3868. next;
  3869. } elsif( $cur =~ /^(id:)(.*)/ ) {
  3870. if($refreshIds==1) {
  3871. if($playlistIds) {
  3872. $playlistIds=$playlistIds.",$2";
  3873. } else {
  3874. $playlistIds=$2;
  3875. }
  3876. $hash->{helper}{playlistIds}=$playlistIds;
  3877. }
  3878. $lastId=$2; # CD 0030
  3879. next;
  3880. # CD 0030 start
  3881. } elsif( $cur =~ /^(url:)(.*)/ ) {
  3882. if($refreshIds==1) {
  3883. if ($lastId<0) {
  3884. $hash->{helper}{playlistUrls}{$lastId}=$2;
  3885. }
  3886. }
  3887. next;
  3888. # CD 0030 end
  3889. # CD 0014 end
  3890. } else {
  3891. next;
  3892. }
  3893. }
  3894. $hash->{helper}{playerStatusOK}=1; # CD 0042
  3895. # Matthew 0019 start
  3896. if( $hash->{SYNCED} ne "yes") {
  3897. readingsBulkUpdate( $hash, "synced", "none" );
  3898. }
  3899. # Matthew 0019 end
  3900. # CD 0003 moved before readingsEndUpdate
  3901. # update the cover art
  3902. SB_PLAYER_CoverArt( $hash );
  3903. # CD 0006 - deaktiviert, SB_PLAYER_ParsePlayerStatus kann nur von SB_PLAYER_Parse aufgerufen werden, dort ist readingsBeginUpdate aber bereits aktiv
  3904. #readingsEndUpdate( $hash, 1 );
  3905. }
  3906. # CD 0018 start
  3907. # ----------------------------------------------------------------------------
  3908. # convert MAC to (LMS) playername
  3909. # ----------------------------------------------------------------------------
  3910. sub SB_PLAYER_MACToLMSPlayername( $$ ) {
  3911. my( $hash, $mac ) = @_;
  3912. my $name = $hash->{NAME};
  3913. return $hash->{PLAYERNAME} if($hash->{PLAYERMAC} eq $mac);
  3914. my $dev;
  3915. foreach my $e ( keys %{$hash->{helper}{SB_PLAYER_SyncMasters}} ) {
  3916. if(defined($hash->{helper}{SB_PLAYER_SyncMasters}{$e}{MAC})&&($mac eq $hash->{helper}{SB_PLAYER_SyncMasters}{$e}{MAC})) { # 0056 defined hinzugefügt
  3917. $dev=$e;
  3918. last;
  3919. }
  3920. }
  3921. return $dev;
  3922. }
  3923. # CD 0018 end
  3924. # CD 0014 start
  3925. # ----------------------------------------------------------------------------
  3926. # estimate elapsed time
  3927. # ----------------------------------------------------------------------------
  3928. sub SB_PLAYER_EstimateElapsedTime( $ ) {
  3929. my( $hash ) = @_;
  3930. my $name = $hash->{NAME};
  3931. my $d=ReadingsVal($name,"duration",0);
  3932. # nur wenn duration>0
  3933. if(($d ne '?')&&($d>0)) { # CD 0033 check for '?'
  3934. # wenn {helper}{elapsedTime} bekannt ist als Basis verwenden
  3935. if((defined($hash->{helper}{elapsedTime}))&&($hash->{helper}{elapsedTime}{VAL}>0)) {
  3936. $hash->{helper}{elapsedTime}{VAL}=$hash->{helper}{elapsedTime}{VAL}+(gettimeofday()-$hash->{helper}{elapsedTime}{TS});
  3937. $hash->{helper}{elapsedTime}{TS}=gettimeofday();
  3938. } else {
  3939. my $dTS=time_str2num(ReadingsTimestamp($name,"duration",0));
  3940. my $n=gettimeofday();
  3941. if(($n-$dTS)<=$d) {
  3942. $hash->{helper}{elapsedTime}{VAL}=gettimeofday()-$dTS;
  3943. $hash->{helper}{elapsedTime}{TS}=gettimeofday();
  3944. } else {
  3945. $hash->{helper}{elapsedTime}{VAL}=$d;
  3946. $hash->{helper}{elapsedTime}{TS}=gettimeofday();
  3947. }
  3948. }
  3949. } else {
  3950. delete($hash->{helper}{elapsedTime}) if(defined($hash->{helper}{elapsedTime}));
  3951. }
  3952. }
  3953. # CD 0014 end
  3954. # ----------------------------------------------------------------------------
  3955. # update the volume readings
  3956. # ----------------------------------------------------------------------------
  3957. sub SB_PLAYER_UpdateVolumeReadings( $$$ ) {
  3958. my( $hash, $vol, $bulk ) = @_;
  3959. my $name = $hash->{NAME};
  3960. $vol = int($vol); # MM 0016, Fix wegen AirPlay-Plugin
  3961. if( $bulk == true ) {
  3962. readingsBulkUpdate( $hash, "volumeStraight", $vol );
  3963. if( $vol > 0 ) {
  3964. readingsBulkUpdate( $hash, "volume", $vol );
  3965. } else {
  3966. readingsBulkUpdate( $hash, "volume", "muted" );
  3967. }
  3968. } else {
  3969. readingsSingleUpdate( $hash, "volumeStraight", $vol, 0 );
  3970. if( $vol > 0 ) {
  3971. readingsSingleUpdate( $hash, "volume", $vol, 0 );
  3972. } else {
  3973. readingsSingleUpdate( $hash, "volume", "muted", 0 );
  3974. }
  3975. }
  3976. return;
  3977. }
  3978. # ----------------------------------------------------------------------------
  3979. # set volume of synced players
  3980. # ----------------------------------------------------------------------------
  3981. sub SB_PLAYER_SetSyncedVolume( $$ ) {
  3982. my( $hash, $vol ) = @_;
  3983. my $name = $hash->{NAME};
  3984. return if (!defined($hash->{SYNCED}) || ($hash->{SYNCED} ne "yes"));
  3985. my $sva=AttrVal($name, "syncVolume", undef);
  3986. my $t=$hash->{SYNCGROUP}.",".$hash->{SYNCMASTER};
  3987. $vol = int($vol); # MM 0016, Fix wegen AirPlay-Plugin
  3988. $hash->{helper}{setSyncVolume}=$vol;
  3989. if(defined($sva) && ($sva ne "0") && ($sva ne "1")) {
  3990. my @pl=split(",",$t);
  3991. my @chlds=devspec2array("TYPE=SB_PLAYER");
  3992. foreach (@pl) {
  3993. if (($_ ne "?") && ($_ ne $hash->{PLAYERMAC})) {
  3994. my $mac=$_;
  3995. foreach(@chlds) {
  3996. my $chash=$defs{$_};
  3997. if(defined($chash) && defined($chash->{PLAYERMAC}) && ($chash->{PLAYERMAC} eq $mac)) {
  3998. my $sva2=AttrVal($chash->{NAME}, "syncVolume", undef);
  3999. if (defined($sva2) && ($sva eq $sva2)) {
  4000. if ($vol>0) { # CD 0010
  4001. if(ReadingsVal($chash->{NAME}, "volumeStraight", $vol)!=$vol) {
  4002. #Log 0,$chash->{NAME}." setting volume to ".$vol." (from ".$hash->{NAME}.")";
  4003. $chash->{helper}{setSyncVolume}=$vol;
  4004. fhem "set ".$chash->{NAME}." volumeStraight ".$vol." x";
  4005. }
  4006. # CD 0010 start
  4007. } else {
  4008. if(ReadingsVal($chash->{NAME}, "volume", "x") ne "muted") {
  4009. #Log 0,$chash->{NAME}." muting (from ".$hash->{NAME}.")";
  4010. IOWrite( $chash, "$chash->{PLAYERMAC} mixer muting 1\n" );
  4011. }
  4012. # CD 0010 end
  4013. }
  4014. }
  4015. }
  4016. }
  4017. }
  4018. }
  4019. }
  4020. return;
  4021. }
  4022. # ----------------------------------------------------------------------------
  4023. # load/save player state
  4024. #
  4025. # CD 0036
  4026. # ----------------------------------------------------------------------------
  4027. sub SB_PLAYER_PlayerStatefileName($)
  4028. {
  4029. my( $name ) = @_;
  4030. my $statefile = $attr{global}{statefile};
  4031. $statefile = substr $statefile,0,rindex($statefile,'/')+1;
  4032. return $statefile ."sbps_$name.dd.save";
  4033. }
  4034. sub SB_PLAYER_SavePlayerStates($)
  4035. {
  4036. my( $hash ) = @_;
  4037. my $name = $hash->{NAME};
  4038. return "No saved playlists found" unless(defined($hash->{helper}{savedPlayerState}));
  4039. return "No statefile specified" if(!$attr{global}{statefile});
  4040. my $statefile = SB_PLAYER_PlayerStatefileName($name);
  4041. if(open(FH, ">$statefile")) {
  4042. my $t = localtime;
  4043. print FH "#$t\n";
  4044. my $dumper = Data::Dumper->new([]);
  4045. $dumper->Terse(1);
  4046. $dumper->Values([$hash->{helper}{savedPlayerState}]);
  4047. print FH $dumper->Dump;
  4048. close(FH);
  4049. } else {
  4050. my $msg = "SB_PLAYER_SavePlayerStates: Cannot open $statefile: $!";
  4051. Log3 $hash, 1, $msg;
  4052. }
  4053. return undef;
  4054. }
  4055. sub SB_PLAYER_LoadPlayerStates($)
  4056. {
  4057. my ($hash) = @_;
  4058. my $name = $hash->{NAME};
  4059. return "No statefile specified" if(!$attr{global}{statefile});
  4060. my $statefile = SB_PLAYER_PlayerStatefileName($name);
  4061. if(open(FH, "<$statefile")) {
  4062. my $encoded;
  4063. while (my $line = <FH>) {
  4064. chomp $line;
  4065. next if($line =~ m/^#.*$/);
  4066. $encoded .= $line;
  4067. }
  4068. close(FH);
  4069. return if( !defined($encoded) );
  4070. my $decoded = eval $encoded;
  4071. $hash->{helper}{savedPlayerState} = $decoded;
  4072. } else {
  4073. my $msg = "SB_PLAYER_LoadPlayerStates: no playlists file found";
  4074. Log3 undef, 4, $msg;
  4075. }
  4076. return undef;
  4077. }
  4078. # ##############################################################################
  4079. # No PERL code beyond this line
  4080. # ##############################################################################
  4081. 1;
  4082. =pod
  4083. =item device
  4084. =item summary control Squeezebox Media Players
  4085. =item summary_DE Ansteuerung von Squeezebox Media Playern
  4086. =begin html
  4087. <a name="SB_PLAYER"></a>
  4088. <h3>SB_PLAYER</h3>
  4089. <ul>
  4090. <a name="SBplayerdefine"></a>
  4091. <b>Define</b>
  4092. <ul>
  4093. <code>define &lt;name&gt; SB_PLAYER &lt;player_mac_address&gt; [ampl:&lt;ampl&gt;] [coverart:&lt;coverart&gt;]</code>
  4094. <br><br>
  4095. This module controls Squeezebox Media Players connected to a defined Logitech Media Server (LMS). A SB_SERVER device is required.<br>
  4096. Usually SB_PLAYER devices are created automatically by autocreate.<br><br>
  4097. <ul>
  4098. <li><code>&lt;player_mac_address&gt;</code>: Mac address of player as seen by LMS.</li>
  4099. </ul><br>
  4100. <b>Optional</b><br><br>
  4101. <ul>
  4102. <li><code>&lt;[ampl]&gt;</code>: A FHEM device to switch on or off when relevant events are received. The attribute
  4103. <a href="#SBplayeramplifier">amplifier</a> specifies whether to switch this device upon reception of on/off or play/pause/stop events.</li>
  4104. <li><code>&lt;[coverart]&gt;</code>: A FHEM weblink to be updated with the current coverart.
  4105. This may be used to show coverart in a floorplan.</li>
  4106. </ul><br><br>
  4107. </ul>
  4108. <a name="SBplayerset"></a>
  4109. <b>Set</b>
  4110. <ul>
  4111. <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code>
  4112. <br><br>
  4113. SB_Player related commands:<br><br>
  4114. <ul>
  4115. <li><b>play</b> - start playback (might not work unless previously paused).</li>
  4116. <li><b>pause [0|1]</b> - toggle between play and pause states. &ldquo;pause 1&rdquo; and &ldquo;pause 0&rdquo; respectively pause and resume play unconditionally.</li>
  4117. <li><b>stop</b> - stop playback</li>
  4118. <li><b>next|channelUp</b> - next track</li>
  4119. <li><b>prev|channelDown</b> - previous track or the beginning of the current track.</li>
  4120. <li><b>mute</b> - toggle mute.</li>
  4121. <li><b>volume &lt;n&gt;</b> - set volume to &lt;n&gt;. &lt;n&gt; must be a number between 0 and 100</li>
  4122. <li><b>volumeStraight &lt;n&gt;</b> - same as volume</li>
  4123. <li><b>volumeDown</b> - reduce the volume by a number of steps given with the attribute volumeStep; default 10 steps</li>
  4124. <li><b>volumeUp</b> - increase the volume by a number of steps given with the attribute volumeStep; default 10 steps</li>
  4125. <li><b>on</b> - turn player on if possible. Issue play command otherwise.</li>
  4126. <li><b>off</b> - turn player off if possible. Issue stop command otherwise.</li>
  4127. <li><b>shuffle off|song|album</b> - enable/disable shuffle mode.</li>
  4128. <li><b>repeat one|all|off</b> - Set repeat mode.</li>
  4129. <li><b>sleep &lt;timespec&gt;</b> - Set player off after &lt;timespec&gt; has expired, fading player volume down. &lt;timespec&gt;'s format is hh:mm:ss.
  4130. &lt;timespec&gt; is a relative time specification. That means: <code>set &lt;myPlayer&gt; sleep 22:00:00</code> doesn’t switch the player off at 10pm but after 22 hours</li>
  4131. <li><b>favorites &lt;favorite&gt;</b> - Empty current playlist and start &lt;favorite&gt;.
  4132. &lt;favorite&gt; is a selected favorite from out of the favorite-list. The frontend may make favorites selectable by a dropdown list.</li>
  4133. <li><b>talk|sayText &lt;text&gt;</b> - Save the playlist, speak &lt;text&gt; using Google TTS and resume saved playlist.</li>
  4134. <li><b>playlist track|album|artist|genre|year &lt;x&gt;</b> - Empty current playlist and play given argument.</li>
  4135. <li><b>playlist genre:&lt;genre&gt; artist:&lt;artist&gt; album:&lt;album&gt;</b> - Empty current playlist and play all tracks matching the parameters. A * acts as a wildcard for everything.</li>
  4136. <br>Example:<br>
  4137. <code>set myplayer playlist genre:* artist:Whigfield album:*</code><br><br>
  4138. <li><b>playlist play &lt;filename|playlistname&gt;</b> - Empty current playlist and start the track or playlist.</li>
  4139. <li><b>playlist add &lt;filename|playlistname&gt;</b> - Add the specified file or playlist at the end of the current playlist.</li>
  4140. <li><b>playlist insert &lt;filename|playlistname&gt;</b> - Insert specified file or playlist after the current track into
  4141. the current playlist.</li>
  4142. <li><b>statusRequest</b> - Update all readings.</li>
  4143. <li><b>sync &lt;playerName[,playerName...]&gt; [new|asSlave]</b> - Put playerName(s) into this player's multiroom group. Remove playerName(s) from their existing group(s) if necessary. Options:</li>
  4144. <ul>
  4145. <li>new - create a new group, removing this/these player(s) from any group.</li>
  4146. <li>asSlave - add this player to a player or existing group</li>
  4147. </ul><br>
  4148. Examples:<br>
  4149. <code>set playerA sync playerB</code>&nbsp;&nbsp;&nbsp;&nbsp;add playerB to playerA's group, both playing the content of playerA<br>
  4150. <code>set playerA sync playerB,playerC,playerD</code>&nbsp;&nbsp;&nbsp;&nbsp;add playerB, C and D to playerA's group, all playing the content of playerA<br>
  4151. <code>set playerA sync playerB new</code>&nbsp;&nbsp;&nbsp;&nbsp;create a new group with playerA and B, both playing playerA’s content<br>
  4152. <code>set playerA sync playerB asSlave</code>&nbsp;&nbsp;&nbsp;&nbsp;add playerA to playerB's group, both playing now playerB’s content<br><br>
  4153. <li><b>unsync</b> - Remove this player from any multiroom group</li>
  4154. <li><b>playlists &lt;name&gt;</b> - Empty current playlist and start selected playlist.</li>
  4155. <li><b>cliraw &lt;command&gt;</b> - Tell LMS to execute &lt;command&gt; using its command line interface.
  4156. The answer of the LMS ist written to the internal LASTANSWER.</li>
  4157. <li><b>save [&lt;name&gt;]</b> - Save player state</li>
  4158. <li><b>recall [&lt;name&gt;] [options] </b> - Recall a saved player state. Options:</li>
  4159. <ul>
  4160. <li>del - delete saved state after restore</li>
  4161. <li>delonly - delete saved state without restoring</li>
  4162. <li>off - ignore saved power setting, turn player off after restore</li>
  4163. <li>on - ignore saved power setting, turn player on after restore</li>
  4164. <li>play - ignore saved play state, start playing after restore</li>
  4165. <li>stop - ignore saved play state, stop playing after restore</li>
  4166. </ul>
  4167. <li><b>show line1:&lt;line1&gt; line2:[&lt;line2&gt;] duration:&lt;duration&gt;</b> - show text on the display of the player for a certain duration.
  4168. If no 2. row should be displayed, the command however must contain 'line2:'</li>
  4169. <ul>
  4170. <li>line1 - Text to be displayed in the first line</li>
  4171. <li>line2 - Text to be displayed in the second line</li>
  4172. <li>duration - Duration of display in seconds</li>
  4173. </ul>
  4174. <li><b>CurrentTrackPosition &lt;TimePosition&gt;</b> - Sets the current timeposition inside the title to the given value.</li>
  4175. <li><b>resetTTS</b> - Resets TTS, can be necessary if the output via Google blocks.</li>
  4176. </ul>
  4177. <br>Alarms<br>
  4178. <ul>
  4179. Multiple alarms may be defined.
  4180. <li><b>allalarms add &lt;weekdays&gt; &lt;alarm time&gt; [&lt;playlist|URL&gt;]</b> - Add a new alarm</li>
  4181. <br>&lt;weekdays&gt; - Active days for this alarm. Format: 0..7|daily|all<br>
  4182. 0..6 for Sunday (0) through Saturday (6), 7 for every day<br>
  4183. The first two letters of the english or german names may be used to specify days instead of a single digit. (Su/So, Mo, Tu/Di, We/Mi, Th/Do, Fr, Sa)<br>
  4184. &lt;alarm time&gt; Alarm time specified as hh:mm[:ss]<br>&lt;playlist|URL&gt; If this parameter in not set, the playlist CURRENT is used.<br><br>
  4185. Example:<br>
  4186. <code>set player allalarms add 1DiWe 06:30 AlarmPlaylist</code> - Add a new alarm to sound playlist AlarmPlaylist every Monday through Wednesday at 06:30<br><br>
  4187. <li><b>allalarms enable|disable</b> - Set global enable/disable flag. Setting this to "disable" effectively
  4188. turns off all alarms. Setting this to "enable" allows alarms to honor their individual flags.
  4189. Identical to command alarmsEnabled on|off.</li>
  4190. <li><b>allalarms delete</b> - Delete all alarms.</li>
  4191. <li><b>allalarms statusRequest</b> - Update status of all alarms.</li>
  4192. <li><b>alarmsSnooze &lt;minutes&gt;</b> - Set duration of any snooze in minutes.</li>
  4193. <li><b>alarmsTimeout &lt;minutes&gt;</b> - Set duration of alarms in minutes. Setting this to 0 will disable the automatic timeout.</li>
  4194. <li><b>alarmsDefaultVolume &lt;vol&gt;</b> - Set default volume level (0-100) of alarms. This can be overridden by individual levels per alarm.</li>
  4195. <li><b>alarmsFadeIn on|off</b> - Whether alarms should fade in on this player</li>
  4196. <li><b>alarmsEnabled on|off</b> - Whether any alarm can sound on this player. Set to off to prevent any alarm from sounding; on to allow them to sound</li>
  4197. <li><b>snooze</b> - Switch off a running alarm and start again after a certain time, defined by the reading alarmsSnooze.</li>
  4198. <br>
  4199. <li><b>alarm&lt;X&gt; delete</b> - Delete alarm &lt;X&gt;</li>
  4200. <li><b>alarm&lt;X&gt; volume &lt;n&gt;</b> - Set volume for alarm &lt;X&gt; to &lt;n&gt;</li>
  4201. <li><b>alarm&lt;X&gt; enable|disable</b> - Enable or disable alarm &lt;X&gt;</li>
  4202. <li><b>alarm&lt;X&gt; sound &lt;playlist|URL&gt;</b> - Define playlist or URL to be sounded by alarm &lt;X&gt;</li>
  4203. <li><b>alarm&lt;X&gt; repeat 0|off|no|1|on|yes</b> - Specify one-shot or repeating alarm</li>
  4204. <li><b>alarm&lt;X&gt; wdays &lt;weekdays&gt;</b> - Specify active days for alarm &lt;X&gt;. Format: 0..7|daily|all<br>
  4205. 0..6 for Sunday (0) through Saturday (6), 7 for every day<br>
  4206. The first two letters of the english or german names may be used to specify days instead of a single digit. (Su/So, Mo, Tu/Di, We/Mi, Th/Do, Fr, Sa)</li>
  4207. <li><b>alarm&lt;X&gt; time hh:mm[:ss]</b> - Define alarm time</li>
  4208. </ul>
  4209. </ul>
  4210. <br>
  4211. <br><br>
  4212. <a name="SBplayerattr"></a>
  4213. <b>Attributes</b>
  4214. <ul>
  4215. <li>IODev &lt;servername&gt;<br>
  4216. FHEM-Name of the SB_SERVER device controlling this player.</li>
  4217. <li><a href="#do_not_notify">do_not_notify</a> true|false<br>
  4218. Disable FileLog/notify/inform notification for a device. This affects the received signal, the set and trigger commands.
  4219. Possible values are true|false</li>
  4220. <li><a name="SBplayeramplifier">amplifier</a> on|play<br>
  4221. Configure trigger for amplifier device. Possible values:
  4222. <ul>
  4223. <li><code>on</code>: Switch on "on" and "off" events.</li>
  4224. <li><code>play</code>: Switch on "play", "pause" and "stop" events.</li>
  4225. </ul>
  4226. </li>
  4227. <li>amplifierDelayOff &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4228. Delay in seconds before turning the amplifier off after the player has stopped or been turned off. A second comma separated delay optionally enables switching off the amplifier after receiving a pause event.</li>
  4229. <li>coverartheight 50|100|200<br>Display the coverart with a height of 50, 100 or 200 pixels</li>
  4230. <li>coverartwidth 50|100|200<br>Display the coverart with a width of 50, 100 or 200 pixels</li>
  4231. <li><a href="#event-on-change-reading">event-on-change-reading</a> &lt;expression&gt;<br><br>
  4232. Example:<br>
  4233. <code>attr &lt;playername&gt; event-on-change-reading currentAlbum, currentArtist, currentTitle</code><br>
  4234. Only changes in the readings currentAlbum, currentArtist, currentTitle cause an event.</li><br>
  4235. <li>fadeinsecs &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4236. Fade-in period in seconds. A second comma separated value optionally specifies the period to use on unpause.</li>
  4237. <li>idismac true|false<br>
  4238. Determines if the MAC-adress should be the unique identifier</li>
  4239. <li>statusRequestInterval &lt;sec&gt;<br>
  4240. Interval in seconds for automatic status requests. Default: 300</li>
  4241. <li>syncedNamesSource FHEM|LMS<br>
  4242. Determines if the synced reading contains the LMS or FHEM names of the players</li>
  4243. <li>syncVolume 0|1<br>
  4244. Defines whether the loudness of this player is synchronized with the other players of its group</li>
  4245. <li>ttsAPIKey &lt;API-key&gt;<br>
  4246. For the use of T2Speech from the company VoiceRSS (voicerss.org) an API-key is needed. Not needed with Google’s T2Speech.</li>
  4247. <li>ttsDelay &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4248. Delay in seconds before starting text to speech playback. A second comma separated delay may optionally be given to be used if the player is off.</li>
  4249. <li>ttslanguage de|en|fr<br>
  4250. Specifies the language of the voice output. A complete list of available languages using Google’s TTS can be found at wikipedia.org.</li>
  4251. <li>ttslink &lt;link&gt;<br>
  4252. Enter the link to the TTS-Funktion. Google, VoiceRSS and –with reservations- Text2Speech are supported.<br><br>
  4253. Example of using Google’s TTS:<br>
  4254. <code>attr &lt;playername&gt; ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=&lt;LANG&gt;&q=&lt;TEXT&gt;&client=tw-ob</code></li><br>
  4255. <li>ttsMP3FileDir &lt;directory&gt;<br>
  4256. Directory to be used by default for text-embedded MP3-Files.</li><br>
  4257. Example:<br>
  4258. <code>attr &lt;SB_device&gt; ttsMP3FileDir /volume1/music/sounds</code><br><br>
  4259. <li>ttsOptions &lt;options&gt;<br>various options, comma-separated, for TTS-output.
  4260. <br><br>Options:<ul>
  4261. <li>debug - Write additional information into the FHEM-logfile</li>
  4262. <li>debugsaverestore - Additional information about loading/saving of player‘s status are written into the logfile</li>
  4263. <li>nosaverestore - Do not save and restore the status of the player, thereby the normal playing stops after using TTS.</li>
  4264. <li>forcegroupon - Switch on all players of the group.</li>
  4265. <li>ignorevolumelimit - Ignore the attribute volumeLimit while using TTS-output</li>
  4266. <li>doubleescape - Alternative encoding for letters with accents</li>
  4267. </ul></li>
  4268. <li>ttsPrefix &lt;text&gt;<br>
  4269. Text prepended to every text to speech output</li>
  4270. <li>ttsVolume &lt;value&gt;<br>
  4271. Volume for text to speech. Defaults to current volume. If the attribute <code>ttsoptions</code> contains <code>ignorevolumelimit</code>
  4272. any volume limit will be ignored for text to speech. Possible values: 0-100</li>
  4273. <li>updateReadingsOnSet true|false<br>
  4274. If set to true, most readings are immediately updated when a set command is executed without waiting for a reply from the server.</li>
  4275. <li>volumeLimit &lt;value&gt;<br>
  4276. Upper limit for volume setting by FHEM.</li>
  4277. <li>volumeStep &lt;value&gt;<br>
  4278. Sets the volume adjustment to a granularity given by &lt;value&gt;. Possible values: 1–100. Default value: 10</li>
  4279. </ul>
  4280. </ul>
  4281. =end html
  4282. =begin html_DE
  4283. <a name="SB_PLAYER"></a>
  4284. <h3>SB_PLAYER</h3>
  4285. <ul>
  4286. <a name="SBplayerdefine"></a>
  4287. <b>Define</b>
  4288. <ul>
  4289. <code>define &lt;name&gt; SB_PLAYER &lt;player_mac_address&gt; [ampl:&lt;ampl&gt;] [coverart:&lt;coverart&gt;]</code>
  4290. <br><br>
  4291. Dieses Modul steuert Squeezebox-Player, die mit einem bereits definiertem
  4292. Logitech Media Server (LMS) verbunden sind. Ein SB_SERVER-Device ist erforderlich.
  4293. Normalerweise wird das SB_PLAYER Device automatisch durch autocreate erzeugt.<br><br>
  4294. <ul>
  4295. <li><code>&lt;player_mac_address&gt;</code>: Die MAC-Adresse, wie der LMS den Player sieht.</li>
  4296. </ul><br>
  4297. <b>Optional</b><br><br>
  4298. <ul>
  4299. <li><code>&lt;[ampl]&gt;</code>: Ein FHEM-Device welches ein- oder ausgeschaltet werden soll, wenn
  4300. ein passendes Event empfangen wird. Das Attribut
  4301. <a href="#SBplayeramplifier">amplifier</a> gibt an, ob das passende Event on/off oder play/pause sein soll.</li>
  4302. <li><code>&lt;[coverart]&gt;</code>: Unter &lt;coverart&gt; gibt man den Namen eines FHEM weblink image
  4303. Elementes an. Der Player aktualisiert dann jeweils den Link auf das aktuelle Coverart Bild, so dass
  4304. man dieses z.B. im Floorplan anzeigen kann.</li>
  4305. </ul><br><br>
  4306. </ul>
  4307. <a name="SBplayerset"></a>
  4308. <b>Set</b>
  4309. <ul>
  4310. <code>set &lt;Name&gt; &lt;Befehl&gt; [&lt;Parameter&gt;]</code>
  4311. <br><br>
  4312. Befehle zur Steuerung des Players:<br><br>
  4313. <ul>
  4314. <li><b>play</b> - Startet die Wiedergabe (Es kann sein, dass die Wiedergabe nicht funktioniert,
  4315. wenn kein pause-Befehl vorangegangen ist).</li>
  4316. <li><b>pause [0|1]</b> - Schaltet zwischen play und pause hin und her (Toggle-Funktion).
  4317. &ldquo;pause 1&rdquo; und &ldquo;pause 0&rdquo; schaltet bedingungslos auf Pause resp. Wiedergabe.</li>
  4318. <li><b>stop</b> - Stoppt die Wiedergabe</li>
  4319. <li><b>next|channelUp</b> - Springt zum n&auml;chsten Track</li>
  4320. <li><b>prev|channelDown</b> - Springt zum vorherigen Track bzw. zum Anfang des gegenw&auml;rtig gespielten Tracks.</li>
  4321. <li><b>mute</b> - Toggelt die Lautst&auml;rke stumm und wieder zur&uuml;ck.</li>
  4322. <li><b>volume &lt;n&gt;</b> - Stellt die Lautst&auml;rke auf einen Wert &lt;n&gt; ein. Dabei muss &lt;n&gt;
  4323. eine Zahl zwischen 0 und 100 sein.</li>
  4324. <li><b>volumeStraight &lt;n&gt;</b> - mit volume identisch</li>
  4325. <li><b>volumeDown</b> - Verringert die Lautst&auml;rke um eine bestimmte Anzahl von Schritten die mit dem Attribut
  4326. <a href="#SBplayervolumeStep">volumeStep</a> vorgegeben werden. Wird nichts vorgegeben, werden 10 Schritte verwendet</li>
  4327. <li><b>volumeUp</b> - Erh&ouml;ht die Lautst&auml;rke um eine bestimmte Anzahl von Schritten, die mit dem Attribut
  4328. <a href="#SBplayervolumeStep">volumeStep</a> vorgegeben werden. Wird nichts vorgegeben, werden 10 Schritte verwendet</li>
  4329. <li><b>on</b> - Schaltet den Player ein. Ist das nicht m&ouml;glich, wird ein play-Befehl gesendet.</li>
  4330. <li><b>off</b> - Schaltet den Player aus. Ist das nicht m&ouml;glich, wird ein stop-Befehl gesendet.</li>
  4331. <li><b>shuffle off|song|album</b> - Schaltet die zuf&auml;llige Wiedergabe aus (off); f&uuml;r Songs einer Wiedergabeliste (song)
  4332. ein oder f&uuml;r eine Liste von Alben ein(album).</li>
  4333. <li><b>repeat one|all|off</b> - Schaltet die Wiederholung von Tracks einer Wiedergabeliste aus (off), f&uuml;r ein Track
  4334. ein (one) oder f&uuml;r alle St&uuml;cke der Wiedergabeliste ein (all).</li>
  4335. <li><b>sleep &lt;timespec&gt;</b> - Dauer, nach der sich der Player ausschalten soll,
  4336. Format hh:mm[:ss]. Bei der Zeitangabe handelt es sich um eine relative Zeitangabe ab
  4337. dem Aufruf. Das bedeutet:
  4338. <code>set &lt;myPlayer&gt; sleep 22:00:00</code> schaltet den Player nicht um 10 Uhr abends aus,
  4339. sondern erst 22 Stunden nach dem Aufruf!</li>
  4340. <li><b>favorites &lt;favorite&gt;</b> - L&ouml;scht die aktuelle Wiedergabeliste und startet den Favoriten &lt;favorite&gt;.
  4341. &lt;favorite&gt; ist der ausgew&auml;hlte Favorit aus einer Favoritenliste. Das Frontend kann eine
  4342. Dropdownliste zur Verf&uuml;gung stellen, aus der der Favorit ausgew&auml;hlt werden kann.</li>
  4343. <li><b>talk|sayText &lt;text&gt;</b> - Speichert den aktuellen Stand der Playlist, sagt den Text
  4344. &lt;text&gt; unter Zuhilfenahme von GoogleTTS oder VoiceRSS an und setzt die Wiedergabe ab dem
  4345. gespeicherten Punkt fort.</li>
  4346. <li><b>playlist track|album|artist|genre|year &lt;x&gt;</b> - L&ouml;scht die aktuelle Wiedergabeliste
  4347. und gibt das vorgegebene Argument &lt;x&gt; wieder.</li>
  4348. <li><b>playlist genre:&lt;genre&gt; artist:&lt;artist&gt; album:&lt;album&gt;</b> -
  4349. L&ouml;scht die aktuelle Wiedergabeliste und gibt alle Tracks wieder, die den angegebenen Kriterien entsprechen.
  4350. Ein * steht f&uuml;r beliebige Angaben.</li>
  4351. <br>Beispiel:<br>
  4352. <code>set myplayer playlist genre:* artist:Whigfield album:*</code><br><br>
  4353. <li><b>playlist play &lt;filename|playlistname&gt;</b> - L&ouml;scht die aktuelle Wiedergabeliste und
  4354. startet die Wiedergabe eines Track oder einer Playlist.</li>
  4355. <li><b>playlist add &lt;filename|playlistname&gt;</b> - H&auml;ngt die angegebene Datei oder Playlist
  4356. an das Ende der aktuellen Playlist an.</li>
  4357. <li><b>playlist insert &lt;filename|playlistname&gt;</b> - F&uuml;gt die angegebene Datei oder Playlist
  4358. hinter der aktuellen Datei oder Playlist in der aktuellen Playlist ein.</li>
  4359. <li><b>statusRequest</b> - Aktualisierung aller Readings.</li>
  4360. <li><b>sync &lt;playerName[,playerName...]&gt; [new|asSlave]</b> - F&uuml;gt den/die Player mit dem/den Namen
  4361. &lt;playerName&gt; der Multiroom-Gruppe desjenigen Players hinzu, der diesen Befehl aufgerufen hat;
  4362. entfernt aber bei Bedarf den Player &lt;playerName&gt; aus seiner bisherigen Gruppe.<br>Optionen:</li>
  4363. <ul>
  4364. <li>new - Bildet eine neue Gruppe und entfernt den/die Player aus seiner/ihren bisherigen Gruppe/n</li>
  4365. <li>asSlave - F&uuml;gt diesen Player zu einem anderen Player oder einer vorhandenen Gruppe hinzu.</li>
  4366. </ul><br>
  4367. Examples:<br>
  4368. <code>set playerA sync playerB</code>&nbsp;&nbsp;&nbsp;&nbsp;F&uuml;gt PlayerB der Gruppe von PlayerA hinzu;
  4369. beide geben den Inhalt von PlayerA wieder.<br>
  4370. <code>set playerA sync playerB,playerC,playerD</code>&nbsp;&nbsp;&nbsp;&nbsp;F&uuml;gt PlayerB, C and D der
  4371. Gruppe von PlayerA hinzu; alle geben den Inhalt von PlayerA wieder.<br>
  4372. <code>set playerA sync playerB new</code>&nbsp;&nbsp;&nbsp;&nbsp;Bildet eine neue Gruppe mit PlayerA
  4373. und PlayerB, beide geben den Inhalt von PlayerA wieder.<br>
  4374. <code>set playerA sync playerB asSlave</code>&nbsp;&nbsp;&nbsp;&nbsp;F&uuml;gt PlayerA der Gruppe von
  4375. PlayerB hinzu; beide geben den Inhalt von PlayerB wieder.<br><br>
  4376. <li><b>unsync</b> - Entfernt diesen Player aus allen Multiroom-Gruppen</li>
  4377. <li><b>playlists &lt;name&gt;</b> - L&ouml;scht die aktuelle Playlist und startet die ausgew&auml;hlte Playlist.</li>
  4378. <li><b>cliraw &lt;command&gt;</b> - Kann direkte Befehle an das CLI Interface schicken. Die Antwort
  4379. steht dann im Internal LASTANSWER.</li>
  4380. <li><b>save [&lt;name&gt;]</b> - Speichert den derzeitigen Player-Status unter dem Namen &lt;name&gt;.</li>
  4381. <li><b>recall [&lt;name&gt;] [options] </b> - Ruft einen gespeicherten Player-Status auf.<br>Optionen:</li>
  4382. <ul>
  4383. <li>del - L&ouml;scht nach dem Restore den gespeicherten Status</li>
  4384. <li>delonly - L&ouml;scht den gespeicherten Status ohne vorherigem Restore</li>
  4385. <li>off - Beachtet die gespeicherten Einstellungen zum Betriebszustand nicht
  4386. sondern schaltet den Player nach Restore aus.</li>
  4387. <li>on - Beachtet die gespeicherten Einstellungen zum Betriebszustand nicht
  4388. sondern schaltet den Player nach Restore ein.</li>
  4389. <li>play - Beachtet den gespeicherten play-Status nicht sondern startet die Wiedergabe nach Restore.</li>
  4390. <li>stop - Beachtet den gespeicherten play-Status nicht sondern stoppt die Wiedergabe nach Restore.</li>
  4391. </ul>
  4392. <li><b>show line1:&lt;text1&gt; line2:[&lt;text2&gt;] duration:&lt;duration&gt;</b> - Zeigt
  4393. einen beliebigen Text auf dem Display f&uuml;r eine vorgegebene Dauer an. Wenn keine 2. Zeile
  4394. angezeigt werden sollen, muss trotzdem 'line2:' im Aufruf enthalten sein</li>
  4395. <ul>
  4396. <li>text1 - Inhalt der ersten Zeile</li>
  4397. <li>text2 - Inhalt der zweiten Zeile</li>
  4398. <li>duration - Dauer der Anzeige auf dem Display in Sekunden</li>
  4399. </ul>
  4400. <li><b>CurrentTrackPosition &lt;TimePosition&gt;</b> - Setzt die Abspielposition innerhalb des
  4401. Liedes auf den angegebenen Zeitwert (z.B. 0:01:15).</li>
  4402. <li><b>resetTTS</b> - TTS zur&uuml;cksetzen, kann n&ouml;tig sein wenn die Ausgabe über Google h&auml;ngt.</li>
  4403. </ul>
  4404. <br>Befehlsliste zur Steuerung der Wecker, mehrere Wecker k&ouml;nnen definiert werden.<br><br>
  4405. <ul>
  4406. <li><b>allalarms add &lt;weekdays&gt; &lt;alarm time&gt; [&lt;playlist|URL&gt;]</b> - F&uuml;gt einen neuen Wecker hinzu.</li>
  4407. <br>&lt;weekdays&gt; - Aktive Tage f&uuml;r diesen Wecker. Format: [0..7|daily|all]
  4408. 0 bis 6 f&uuml;r Sonntag bis Samstag. 7, daily und all f&uuml;r t&auml;gliches Wecken.
  4409. Die Wochentage k&ouml;nnen ebenfalls durch die ersten zwei Buchstaben des
  4410. Wochentages sowohl in deutscher und englischer Sprache angegeben werden
  4411. (Su/So, Mo, Tu/Di, We/Mi, Th/Do, Fr, Sa).<br><br>
  4412. &lt;alarm time&gt; Weckzeit im Format hh:mm[:ss]<br>
  4413. &lt;playlist|URL&gt; Weckton, wenn dieser Parameter nicht gesetzt wird, wird die aktuelle Playlist verwendet.<br><br>
  4414. Beispiel:<br>
  4415. <code>set player allalarms add 1DiWe 06:30 AlarmPlaylist</code> - F&uuml;gt einen
  4416. neuen Wecker hinzu. Der Wecker startet jeden Montag bis Mittwoch um 6:30 Uhr
  4417. und spielt die Playlist AlarmPlaylist ab.<br><br>
  4418. <li><b>allalarms enable|disable</b> - Setzt das globale einable/disable Flag f&uuml;r alle Wecker.
  4419. "disable" deaktiviert alle Wecker. “enable" gestattet allen
  4420. Weckern sich nach den individuellen Flags zu richten. Identisch mit dem Befehl alarmsEnabled on|off.</li>
  4421. <li><b>allalarms delete</b> - L&ouml;scht alle Wecker.</li>
  4422. <li><b>allalarms statusRequest</b> - Aktualisiert den Zustand aller Wecker.</li>
  4423. <li><b>alarmsSnooze &lt;minutes&gt;</b> - Setzt die Dauer der Weckwiederholung (in Minuten).</li>
  4424. <li><b>alarmsTimeout &lt;minutes&gt;</b> - Bestimmt, wie lange ein Wecker l&auml;uft
  4425. bevor er automatisch beendet wird. Ist der Wert 0, l&auml;uft der Wecker, bis er manuell beendet wird.</li>
  4426. <li><b>alarmsDefaultVolume &lt;vol&gt;</b> - Legt die generelle Lautst&auml;rke (0-100) f&uuml;r den Wecker fest.
  4427. Dieser Wert kann durch eine individuelle Lautst&auml;rkeangabe je Alarm &uuml;berschrieben werden (siehe auch
  4428. <a href="#SBplayeralarmxvolume">alarm&lt;X&gt; volume &lt;n&gt;</a>)</li>
  4429. <li><b>alarmsFadeIn on|off</b> - Schaltet fadeIn f&uuml;r die Wecker ein (on) oder aus (off)</li>
  4430. <li><b>alarmsEnabled on|off</b> - Gibt an, ob die eingetragenen Wecker &uuml;berhaupt abgespielt
  4431. werden sollen (on) oder nicht (off).</li>
  4432. <li><b>snooze</b> - Schaltet einen gerade laufenden Wecker aus und nach einer durch
  4433. das Reading alarmsSnooze definierten Zeit wieder ein..</li>
  4434. <br>
  4435. </ul>
  4436. Befehle f&uuml;r einzelne Wecker (X entspricht der Nummer des Weckers)
  4437. <ul>
  4438. <br>
  4439. <li><b>alarm&lt;X&gt; delete</b> - L&ouml;scht den Wecker &lt;X&gt; aus der Weckerliste.</li>
  4440. <li><b><a name="SBplayeralarmxvolume">alarm&lt;X&gt; volume &lt;n&gt;</a></b> - Stellt
  4441. die Lautst&auml;rke des Weckers &lt;X&gt; auf den Wert &lt;n&gt;.</li>
  4442. <li><b>alarm&lt;X&gt; enable|disable</b> - Aktiviert bzw. deaktiviert den Wecker &lt;X&gt;</li>
  4443. <li><b>alarm&lt;X&gt; sound &lt;playlist|URL&gt;</b> - Bestimmt eine Playlist oder URL, die
  4444. beim Wecker &lt;X&gt; abgespielt werden soll.</li>
  4445. <li><b>alarm&lt;X&gt; repeat 0|off|no|1|on|yes</b> - Bestimmt, ob Wecker &lt;X&gt; einmalig
  4446. (0|off|no) oder mehrfach (1|on|yes) ausgel&ouml;st werden soll.</li>
  4447. <li><b>alarm&lt;X&gt; wdays &lt;weekdays&gt;</b> - Aktive Tage f&uuml;r diesen Wecker.
  4448. &lt;weekdays&gt; hat die m&ouml;glichen Inhalte 0 bis 6 f&uuml;r Sonntag bis Samstag, 7,
  4449. daily und all f&uuml;r t&auml;gliches Wecken. Die Wochentage k&ouml;nnen ebenfalls durch die ersten
  4450. zwei Buchstaben des Wochentages sowohl in deutscher und englischer Sprache angegeben
  4451. werden (Su/So, Mo, Tu/Di, We/Mi, Th/Do, Fr, Sa).</li>
  4452. <li><b>alarm&lt;X&gt; time hh:mm[:ss]</b> - Weckzeit im Format hh:mm[:ss].</li>
  4453. </ul>
  4454. </ul>
  4455. <br>
  4456. <a name="SBplayerattr"></a>
  4457. <b>Attribute</b>
  4458. <ul>
  4459. <li>IODev &lt;servername&gt;<br>
  4460. FHEM-Name des SB_SERVERS.</li>
  4461. <li><a href="#do_not_notify">do_not_notify</a> true|false<br>
  4462. Mit diesem Attribut kann man einstellen, ob der Player ein FHEM Notify bei jeder &auml;nderung eines Readings lostritt oder nicht.</li>
  4463. <li><a name="SBplayeramplifier">amplifier</a> on|play<br>
  4464. Gibt an, mit welcher Funktion ein zuschaltbarer Verst&auml;rkers eingeschaltet werden soll, entweder bei einem
  4465. Einschaltvorgang (on) oder bei einen Startvorgang zum Abspielen eines Files (play).
  4466. </li>
  4467. <li>amplifierDelayOff &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4468. Stellt eine Verz&ouml;gerung zwischen dem Ausschaltvorgang des Players und des Verst&auml;rkers in Sekunden ein.
  4469. Wird eine optionale zweite Zeit durch Komma getrennt angegeben, so wird diese bei Pause verwendet. Fehlt
  4470. die zweite Zeitangabe, wird bei Pause nicht abgeschaltet.</li>
  4471. <li>coverartheight 50|100|200<br>Stellt das Cover mit einer vertikalen Aufl&ouml;sung von 50, 100 oder 200 Pixel dar.</li>
  4472. <li>coverartwidth 50|100|200<br>Stellt das Cover mit einer horizontalen Aufl&ouml;sung von 50, 100 oder 200 Pixel dar.</li>
  4473. <li><a href="#event-on-change-reading">event-on-change-reading</a> &lt;expression&gt;<br>Wird <a href="#do_not_notify">do_not_notify</a>
  4474. auf false gesetzt, veranlasst jede &Auml;nderung eines Readings ein Notify. Mit diesem Attribut k&ouml;nnen
  4475. ausl&ouml;sende Ereignisse gefiltert werden.<br><br>
  4476. Beispiel:<br>
  4477. <code>attr &lt;playername&gt; event-on-change-reading currentAlbum, currentArtist, currentTitle</code><br>
  4478. Nur &auml;nderungen der Readings currentAlbum, currentArtist, currentTitle erzeugen Events.</li><br>
  4479. <li>fadeinsecs &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4480. Fade in f&uuml;r Beginn von Playlisten und neuen Soundfiles. Bezeichnet die Dauer des Vorganges, in der die
  4481. Lautst&auml;rke auf den vorgegebenen Wert ansteigt und wird in Sekunden angegeben. Ein zweiter, durch Komma
  4482. getrennter optionaler Wert, gibt die Dauer des Fadein beim Verlassen des Pausenzustandes an.</li>
  4483. <li>idismac true|false<br>
  4484. Soll die MAC-Adresse des Players als ID genommen werden, muss true (default) eingetragen werden, sonst false.</li>
  4485. <li>statusRequestInterval &lt;sec&gt;<br>
  4486. Aktualisierungsintervall der automatischen Status-Abfrage. Default: 300</li>
  4487. <li>syncedNamesSource FHEM|LMS<br>
  4488. Legt fest ob die FHEM-Ger&auml;tenamen oder die LMS-Namen der Player im synced-Reading verwendet werden.</li>
  4489. <li>syncVolume 0|1<br>
  4490. Legt fest ob die Lautst&auml;rke des Players mit anderen Playern aus der Gruppe synchronisiert wird.</li>
  4491. <li>ttsAPIKey &lt;API-key&gt;<br>
  4492. F&uuml;r die Benutzung von T2Speech der Firma VoiceRSS (voicerss.org) ist ein API-Key erforderlich.
  4493. F&uuml;r Googles T2Speech ist kein Key notwendig.</li>
  4494. <li>ttsDelay &lt;sec1&gt;[,&lt;sec2&gt;]<br>
  4495. Wartezeit in Sekunden bevor die TTS-Ausgabe gestartet wird. Optional kann eine 2. Zeit durch
  4496. Komma getrennt angegeben werden. In diesem Fall wird die 1. Zeit verwendet wenn der Player
  4497. eingeschaltet ist und die 2. wenn er ausgeschaltet ist.</li>
  4498. <li>ttslanguage de|en|fr<br>
  4499. Stellt die gew&uuml;nschte Sprache f&uuml;r die Text-to-Speech Funktion ein. Eine vollst&auml;ndige Liste
  4500. der verf&uuml;gbaren Sprachen findet sich in wikipedia.org</li>
  4501. <li>ttslink &lt;link&gt;<br>
  4502. Link zur TTS-Funktion. Unterst&uuml;tzt werden: Google, VoiceRSS (API-Key wird ben&ouml;tigt) und mit Einschr&auml;nkungen Text2Speech.<br><br>
  4503. Beispiel f&uuml;r Google’s TTS:<br>
  4504. <code>attr &lt;playername&gt; ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=&lt;LANG&gt;&q=&lt;TEXT&gt;&client=tw-ob</code></li><br>
  4505. <li>ttsMP3FileDir &lt;directory&gt;<br>
  4506. Standard-Verzeichnis f&uuml;r Musik- und Sprachdateien (z.B. Gong, Sirene, Ansagen,...)
  4507. im mp3-Format, die beim TTS eingebunden werden sollen.</li><br>
  4508. Beispiel:<br>
  4509. <code>attr &lt;SB_device&gt; ttsMP3FileDir /volume1/music/sounds</code><br><br>
  4510. <li>ttsOptions &lt;Optionen&gt;<br>Verschiedene durch Komma getrennte Optionen f&uuml;r die TTS-Ausgabe.
  4511. <br><br>Optionen:<ul>
  4512. <li>debug - Schreibt zus&auml;tzliche Meldungen zum TTS ins Log.</li>
  4513. <li>debugsaverestore - Schreibt zus&auml;tzliche Meldungen zum Speichern/Laden des Playerzustandes ins Log.</li>
  4514. <li>nosaverestore - Zustand des Players nicht sichern und wiederherstellen, dadurch stoppt die Wiedergabe nach Abspielen des TTS.</li>
  4515. <li>forcegroupon - Player in der Gruppe werden eingeschaltet.</li>
  4516. <li>ignorevolumelimit - Attribut volumeLimit f&uuml;r die TTS-Ausgabe ignorieren</li>
  4517. <li>doubleescape - alternative Codierung der Sonderzeichen</li>
  4518. </ul></li>
  4519. <li>ttsPrefix &lt;text&gt;<br>
  4520. Text, der vor jede TTS-Ausgabe gehangen wird</li>
  4521. <li>ttsVolume &lt;value&gt;<br>
  4522. Lautst&auml;rke f&uuml;r die TTS-Ausgabe. Standardwert ist die gegenw&auml;rtige Lautst&auml;rke. Wenn das Attribut
  4523. ttsOption ignorevolumelimit angegeben worden ist, wird das eingestellte Lautst&auml;rkelimit bei
  4524. TTS nicht beachtet.</li>
  4525. <li>updateReadingsOnSet true|false<br>
  4526. Wird ein Befehl ausgef&uuml;hrt, werden die Readings erst aktualisiert, wenn die Antwort des LMS eintrifft.
  4527. Ist dieses Attribut auf true gesetzt, wird die Antwort nicht abgewartet, sondern die Readings werden
  4528. soweit wie m&ouml;glich sofort aktualisiert.</li>
  4529. <li>volumeLimit &lt;value&gt;<br>
  4530. Oberer Grenzwert zur Lautst&auml;rkeeinstellung durch FHEM.</li>
  4531. <li>volumeStep &lt;value&gt;<br>
  4532. Hier wird die Granularit&auml;t der Lautst&auml;rkeregelung festgelegt. Default ist 10</li>
  4533. </ul>
  4534. </ul>
  4535. =end html_DE
  4536. =cut