97_SB_SERVER.pm 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281
  1. # ############################################################################
  2. # $Id: 97_SB_SERVER.pm 12228 2016-10-01 16:43:31Z chrisd70 $
  3. #
  4. # FHEM Module for Squeezebox Servers
  5. #
  6. # ############################################################################
  7. #
  8. # used to interact with Squeezebox server
  9. #
  10. # ############################################################################
  11. #
  12. # Written by bugster_de
  13. #
  14. # Contributions from: Siggi85, Oliv06, ChrisD
  15. #
  16. # ############################################################################
  17. #
  18. # This is absolutley open source. Please feel free to use just as you
  19. # like. Please note, that no warranty is given and no liability
  20. # granted
  21. #
  22. # ############################################################################
  23. #
  24. # we have the following readings
  25. # power on|off
  26. # version the version of the SB Server
  27. # serversecure is the CLI port protected with a passowrd?
  28. #
  29. # ############################################################################
  30. #
  31. # we have the following attributes
  32. # alivetimer time frequency to set alive signals
  33. # maxfavorites maximum number of favorites we handle at FHEM
  34. #
  35. # ############################################################################
  36. # we have the following internals (all UPPERCASE)
  37. # IP the IP of the server
  38. # CLIPORT the port for the CLI interface of the server
  39. #
  40. # ############################################################################
  41. # based on 97_SB_SERVER.pm 9811 beta 0023 CD
  42. # ############################################################################
  43. package main;
  44. use strict;
  45. use warnings;
  46. use IO::Socket;
  47. use URI::Escape;
  48. # include for using the perl ping command
  49. use Net::Ping;
  50. use Encode qw(decode encode); # CD 0009 hinzugefügt
  51. no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  52. # this will hold the hash of hashes for all instances of SB_SERVER
  53. my %favorites;
  54. my $favsetstring = "favorites: ";
  55. # this is the buffer for commands, we queue up when server is power=off
  56. my %SB_SERVER_CmdStack;
  57. # include this for the self-calling timer we use later on
  58. use Time::HiRes qw(gettimeofday time);
  59. use constant { true => 1, false => 0 };
  60. use constant { TRUE => 1, FALSE => 0 };
  61. use constant SB_SERVER_VERSION => '0023';
  62. # ----------------------------------------------------------------------------
  63. # Initialisation routine called upon start-up of FHEM
  64. # ----------------------------------------------------------------------------
  65. sub SB_SERVER_Initialize( $ ) {
  66. my ($hash) = @_;
  67. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  68. # Provider
  69. $hash->{ReadFn} = "SB_SERVER_Read";
  70. $hash->{WriteFn} = "SB_SERVER_Write";
  71. $hash->{ReadyFn} = "SB_SERVER_Ready";
  72. $hash->{Clients} = ":SB_PLAYER:";
  73. my %matchList= (
  74. "1:SB_PLAYER" => "^SB_PLAYER:",
  75. );
  76. $hash->{MatchList} = \%matchList;
  77. # Normal devices
  78. $hash->{DefFn} = "SB_SERVER_Define";
  79. $hash->{UndefFn} = "SB_SERVER_Undef";
  80. $hash->{ShutdownFn} = "SB_SERVER_Shutdown";
  81. $hash->{GetFn} = "SB_SERVER_Get";
  82. $hash->{SetFn} = "SB_SERVER_Set";
  83. $hash->{AttrFn} = "SB_SERVER_Attr";
  84. $hash->{NotifyFn} = "SB_SERVER_Notify";
  85. $hash->{AttrList} = "alivetimer maxfavorites ";
  86. $hash->{AttrList} .= "doalivecheck:true,false ";
  87. $hash->{AttrList} .= "maxcmdstack ";
  88. $hash->{AttrList} .= "httpport ";
  89. $hash->{AttrList} .= "ignoredIPs ignoredMACs internalPingProtocol:icmp,tcp,udp,syn,stream,none "; # CD 0021 none hinzugefügt
  90. $hash->{AttrList} .= $readingFnAttributes;
  91. }
  92. # ----------------------------------------------------------------------------
  93. # called when defining a module
  94. # ----------------------------------------------------------------------------
  95. sub SB_SERVER_Define( $$ ) {
  96. my ($hash, $def ) = @_;
  97. #my $name = $hash->{NAME};
  98. Log3( $hash, 4, "SB_SERVER_Define: called" );
  99. # first of all close existing connections
  100. DevIo_CloseDev( $hash );
  101. my @a = split("[ \t][ \t]*", $def);
  102. # do we have the right number of arguments?
  103. if( ( @a < 3 ) || ( @a > 7 ) ) {
  104. Log3( $hash, 3, "SB_SERVER_Define: falsche Anzahl an Argumenten" );
  105. return( "wrong syntax: define <name> SB_SERVER <serverip[:cliport]>" .
  106. "[USER:username] [PASSWORD:password] " . # CD 0007 changed PASSWord to PASSWORD
  107. "[RCC:RCC_Name] [WOL:WOLName] [PRESENCE:PRESENCEName]" ); # CD 0007 added PRESENCE
  108. }
  109. # remove the name and our type
  110. my $name = shift( @a );
  111. shift( @a );
  112. # assign safe default values
  113. $hash->{IP} = "127.0.0.1";
  114. $hash->{CLIPORT} = 9090;
  115. $hash->{WOLNAME} = "none";
  116. $hash->{PRESENCENAME} = "none"; # CD 0007
  117. $hash->{RCCNAME} = "none";
  118. $hash->{USERNAME} = "?";
  119. $hash->{PASSWORD} = "?";
  120. # parse the user spec
  121. foreach( @a ) {
  122. if( $_ =~ /^(RCC:)(.*)/ ) {
  123. $hash->{RCCNAME} = $2;
  124. next;
  125. } elsif( $_ =~ /^(WOL:)(.*)/ ) {
  126. $hash->{WOLNAME} = $2;
  127. next;
  128. } elsif( $_ =~ /^(PRESENCE:)(.*)/ ) { # CD 0007
  129. $hash->{PRESENCENAME} = $2; # CD 0007
  130. next; # CD 0007
  131. } elsif( $_ =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d{3,5})/ ) {
  132. $hash->{IP} = $1;
  133. $hash->{CLIPORT} = $2;
  134. next;
  135. } elsif( $_ =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ ) {
  136. $hash->{IP} = $1;
  137. $hash->{CLIPORT} = 9090;
  138. next;
  139. } elsif( $_ =~ /^(USER:)(.*)/ ) {
  140. $hash->{USERNAME} = $2;
  141. } elsif( $_ =~ /^(PASSWORD:)(.*)/ ) {
  142. $hash->{PASSWORD} = $2;
  143. } else {
  144. next;
  145. }
  146. }
  147. $hash->{LASTANSWER} = "none";
  148. # used for alive checking of the CLI interface
  149. $hash->{ALIVECHECK} = "?";
  150. # the status of the CLI connection (on / off)
  151. $hash->{CLICONNECTION} = "?";
  152. # preset our attributes
  153. if( !defined( $attr{$name}{alivetimer} ) ) {
  154. $attr{$name}{alivetimer} = 120;
  155. }
  156. if( !defined( $attr{$name}{doalivecheck} ) ) {
  157. $attr{$name}{doalivecheck} = "true";
  158. }
  159. if( !defined( $attr{$name}{maxfavorites} ) ) {
  160. $attr{$name}{maxfavorites} = 30;
  161. }
  162. if( !defined( $attr{$name}{maxcmdstack} ) ) {
  163. $attr{$name}{maxcmdstack} = 200;
  164. }
  165. # the port of the HTTP interface as needed for the coverart url
  166. if( !defined( $attr{$name}{httpport} ) ) {
  167. $attr{$name}{httpport} = "9000";
  168. }
  169. # Preset our readings if undefined
  170. my $tn = TimeNow();
  171. # server on / off
  172. if( !defined( $hash->{READINGS}{power}{VAL} ) ) {
  173. $hash->{READINGS}{power}{VAL} = "?";
  174. $hash->{READINGS}{power}{TIME} = $tn;
  175. }
  176. # the server version
  177. if( !defined( $hash->{READINGS}{serverversion}{VAL} ) ) {
  178. $hash->{READINGS}{serverversion}{VAL} = "?";
  179. $hash->{READINGS}{serverversion}{TIME} = $tn;
  180. }
  181. # is the CLI port secured with password?
  182. if( !defined( $hash->{READINGS}{serversecure}{VAL} ) ) {
  183. $hash->{READINGS}{serversecure}{VAL} = "?";
  184. $hash->{READINGS}{serversecure}{TIME} = $tn;
  185. }
  186. # the maximum number of favorites on the server
  187. if( !defined( $hash->{READINGS}{favoritestotal}{VAL} ) ) {
  188. $hash->{READINGS}{favoritestotal}{VAL} = 0;
  189. $hash->{READINGS}{favoritestotal}{TIME} = $tn;
  190. }
  191. # is a scan in progress
  192. if( !defined( $hash->{READINGS}{scanning}{VAL} ) ) {
  193. $hash->{READINGS}{scanning}{VAL} = "?";
  194. $hash->{READINGS}{scanning}{TIME} = $tn;
  195. }
  196. # the scan in progress
  197. if( !defined( $hash->{READINGS}{scandb}{VAL} ) ) {
  198. $hash->{READINGS}{scandb}{VAL} = "?";
  199. $hash->{READINGS}{scandb}{TIME} = $tn;
  200. }
  201. # the scan already completed
  202. if( !defined( $hash->{READINGS}{scanprogressdone}{VAL} ) ) {
  203. $hash->{READINGS}{scanprogressdone}{VAL} = "?";
  204. $hash->{READINGS}{scanprogressdone}{TIME} = $tn;
  205. }
  206. # the scan already completed
  207. if( !defined( $hash->{READINGS}{scanprogresstotal}{VAL} ) ) {
  208. $hash->{READINGS}{scanprogresstotal}{VAL} = "?";
  209. $hash->{READINGS}{scanprogresstotal}{TIME} = $tn;
  210. }
  211. # did the last scan fail
  212. if( !defined( $hash->{READINGS}{scanlastfailed}{VAL} ) ) {
  213. $hash->{READINGS}{scanlastfailed}{VAL} = "?";
  214. $hash->{READINGS}{scanlastfailed}{TIME} = $tn;
  215. }
  216. # number of players connected to us
  217. if( !defined( $hash->{READINGS}{players}{VAL} ) ) {
  218. $hash->{READINGS}{players}{VAL} = "?";
  219. $hash->{READINGS}{players}{TIME} = $tn;
  220. }
  221. # number of players connected to mysqueezebox
  222. if( !defined( $hash->{READINGS}{players_mysb}{VAL} ) ) {
  223. $hash->{READINGS}{players_mysb}{VAL} = "?";
  224. $hash->{READINGS}{players_mysb}{TIME} = $tn;
  225. }
  226. # number of players connected to other servers in our network
  227. if( !defined( $hash->{READINGS}{players_other}{VAL} ) ) {
  228. $hash->{READINGS}{players_other}{VAL} = "?";
  229. $hash->{READINGS}{players_other}{TIME} = $tn;
  230. }
  231. # number of albums in the database
  232. if( !defined( $hash->{READINGS}{db_albums}{VAL} ) ) {
  233. $hash->{READINGS}{db_albums}{VAL} = "?";
  234. $hash->{READINGS}{db_albums}{TIME} = $tn;
  235. }
  236. # number of artists in the database
  237. if( !defined( $hash->{READINGS}{db_artists}{VAL} ) ) {
  238. $hash->{READINGS}{db_artists}{VAL} = "?";
  239. $hash->{READINGS}{db_artists}{TIME} = $tn;
  240. }
  241. # number of songs in the database
  242. if( !defined( $hash->{READINGS}{db_songs}{VAL} ) ) {
  243. $hash->{READINGS}{db_songs}{VAL} = "?";
  244. $hash->{READINGS}{db_songs}{TIME} = $tn;
  245. }
  246. # number of genres in the database
  247. if( !defined( $hash->{READINGS}{db_genres}{VAL} ) ) {
  248. $hash->{READINGS}{db_genres}{VAL} = "?";
  249. $hash->{READINGS}{db_genres}{TIME} = $tn;
  250. }
  251. # initialize the command stack
  252. $SB_SERVER_CmdStack{$name}{first_n} = 0;
  253. $SB_SERVER_CmdStack{$name}{last_n} = 0;
  254. $SB_SERVER_CmdStack{$name}{cnt} = 0;
  255. $hash->{CMDSTACK}=0; # CD 0007
  256. # assign our IO Device
  257. $hash->{DeviceName} = "$hash->{IP}:$hash->{CLIPORT}";
  258. $hash->{helper}{pingCounter}=0; # CD 0004
  259. $hash->{helper}{lastPRESENCEstate}='?'; # CD 0023
  260. # CD 0009 set module version, needed for reload
  261. $hash->{helper}{SB_SERVER_VERSION}=SB_SERVER_VERSION;
  262. # open the IO device
  263. my $ret;
  264. # CD wait for init_done
  265. if ($init_done>0){
  266. delete($hash->{NEXT_OPEN}) if($hash->{NEXT_OPEN}); # CD 0007 reconnect immediately after modify
  267. # CD 0016 start
  268. if( $hash->{STATE} eq "opened" ) {
  269. DevIo_CloseDev( $hash );
  270. readingsSingleUpdate( $hash, "power", "?", 0 );
  271. $hash->{STATE}="disconnected";
  272. }
  273. # CD 0016 end
  274. $ret= DevIo_OpenDev($hash, 0, "SB_SERVER_DoInit" );
  275. }
  276. # do and update of the status
  277. # CD disabled
  278. #InternalTimer( gettimeofday() + 10,
  279. # "SB_SERVER_Alive",
  280. # $hash,
  281. # 0 );
  282. Log3( $hash, 4, "SB_SERVER_Define: leaving" );
  283. return $ret;
  284. }
  285. # ----------------------------------------------------------------------------
  286. # called when deleting a module
  287. # ----------------------------------------------------------------------------
  288. sub SB_SERVER_Undef( $$ ) {
  289. my ($hash, $arg) = @_;
  290. my $name = $hash->{NAME};
  291. Log3( $hash, 4, "SB_SERVER_Undef: called" );
  292. # no idea what this is for. Copied from 10_TCM.pm
  293. # presumably to notify the clients, that the server is gone
  294. foreach my $d (sort keys %defs) {
  295. if( ( defined( $defs{$d} ) ) &&
  296. ( defined( $defs{$d}{IODev} ) ) &&
  297. ( $defs{$d}{IODev} == $hash ) ) {
  298. delete $defs{$d}{IODev};
  299. }
  300. }
  301. # terminate the CLI session
  302. DevIo_SimpleWrite( $hash, "listen 0\n", 0 );
  303. DevIo_SimpleWrite( $hash, "exit\n", 0 );
  304. # close the device
  305. DevIo_CloseDev( $hash );
  306. # remove all timers we created
  307. RemoveInternalTimer( $hash );
  308. return( undef );
  309. }
  310. # ----------------------------------------------------------------------------
  311. # Shutdown function - called before fhem shuts down
  312. # ----------------------------------------------------------------------------
  313. sub SB_SERVER_Shutdown( $$ ) {
  314. my ($hash, $dev) = @_;
  315. Log3( $hash, 4, "SB_SERVER_Shutdown: called" );
  316. # terminate the CLI session
  317. DevIo_SimpleWrite( $hash, "listen 0\n", 0 );
  318. DevIo_SimpleWrite( $hash, "exit\n", 0 );
  319. # close the device
  320. DevIo_CloseDev( $hash );
  321. # remove all timers we created
  322. RemoveInternalTimer( $hash );
  323. return( undef );
  324. }
  325. # ----------------------------------------------------------------------------
  326. # ReadyFn - called when?
  327. # ----------------------------------------------------------------------------
  328. sub SB_SERVER_Ready( $ ) {
  329. my ($hash) = @_;
  330. my $name = $hash->{NAME};
  331. #Log3( $hash, 4, "SB_SERVER_Ready: called" );
  332. # check for bad/missing password
  333. if (defined($hash->{helper}{SB_SERVER_LMS_Status})) {
  334. if (time()-($hash->{helper}{SB_SERVER_LMS_Status})<2) {
  335. if( ( $hash->{USERNAME} ne "?" ) &&
  336. ( $hash->{PASSWORD} ne "?" ) ) {
  337. $hash->{LASTANSWER}='invalid username or password ?';
  338. Log( 1, "SB_SERVER($name): invalid username or password ?" );
  339. } else {
  340. $hash->{LASTANSWER}='missing username and password ?';
  341. Log( 1, "SB_SERVER($name): missing username and password ?" );
  342. }
  343. $hash->{NEXT_OPEN}=time()+60;
  344. }
  345. delete($hash->{helper}{SB_SERVER_LMS_Status});
  346. }
  347. # we need to re-open the device
  348. if( $hash->{STATE} eq "disconnected" ) {
  349. if( ( ReadingsVal( $name, "power", "on" ) eq "on" ) ||
  350. ( ReadingsVal( $name, "power", "on" ) eq "?" ) ) {
  351. # obviously the first we realize the Server is off
  352. # clean up first
  353. RemoveInternalTimer( $hash );
  354. readingsSingleUpdate( $hash, "power", "off", 1 );
  355. $hash->{CLICONNECTION} = "off"; # CD 0007
  356. # and signal to our clients
  357. SB_SERVER_Broadcast( $hash, "SERVER", "OFF" );
  358. }
  359. # CD added init_done
  360. if ($init_done>0) {
  361. # CD 0007 faster reconnect after WOL, use PRESENCE
  362. my $reconnect=0;
  363. if(defined($hash->{helper}{WOLFastReconnectUntil})) {
  364. $hash->{TIMEOUT}=1;
  365. if (time() > $hash->{helper}{WOLFastReconnectNext}) {
  366. delete($hash->{NEXT_OPEN}) if($hash->{NEXT_OPEN});
  367. $hash->{helper}{WOLFastReconnectNext}=time()+15;
  368. $reconnect=1;
  369. }
  370. if (time() > $hash->{helper}{WOLFastReconnectUntil}) {
  371. delete($hash->{TIMEOUT});
  372. delete($hash->{helper}{WOLFastReconnectUntil});
  373. delete($hash->{helper}{WOLFastReconnectNext});
  374. }
  375. }
  376. if( ReadingsVal( $hash->{PRESENCENAME}, "state", "present" ) eq "present" ) {
  377. $reconnect=1;
  378. }
  379. if ($reconnect==1) {
  380. return( DevIo_OpenDev( $hash, 1, "SB_SERVER_DoInit") );
  381. } else {
  382. return undef;
  383. }
  384. } else {
  385. return undef;
  386. }
  387. }
  388. }
  389. # ----------------------------------------------------------------------------
  390. # Get functions
  391. # ----------------------------------------------------------------------------
  392. sub SB_SERVER_Get( $@ ) {
  393. my ($hash, @a) = @_;
  394. my $name = $hash->{NAME};
  395. Log3( $hash, 4, "SB_SERVER_Get: called" );
  396. if( @a != 2 ) {
  397. return( "\"get $name\" needs one parameter" );
  398. }
  399. return( "?" );
  400. }
  401. # ----------------------------------------------------------------------------
  402. # Attr functions
  403. # ----------------------------------------------------------------------------
  404. sub SB_SERVER_Attr( @ ) {
  405. my $cmd = shift( @_ );
  406. my $name = shift( @_ );
  407. my $hash = $defs{$name};
  408. my @args = @_;
  409. Log( 4, "SB_SERVER_Attr($name): called with @args" );
  410. if( $cmd eq "set" ) {
  411. if( $args[ 0 ] eq "alivetimer" ) {
  412. # CD 0021 start
  413. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  414. InternalTimer( gettimeofday() + $args[ 1 ],
  415. "SB_SERVER_tcb_Alive",
  416. "SB_SERVER_Alive:$name",
  417. 0 );
  418. # CD 0021 end
  419. }
  420. # CD 0015 bei Änderung des Ports diesen an Clients schicken
  421. if( $args[ 0 ] eq "httpport" ) {
  422. SB_SERVER_Broadcast( $hash, "SERVER",
  423. "IP " . $hash->{IP} . ":" .
  424. $args[ 1 ] );
  425. }
  426. }
  427. }
  428. # ----------------------------------------------------------------------------
  429. # Set function
  430. # ----------------------------------------------------------------------------
  431. sub SB_SERVER_Set( $@ ) {
  432. my ($hash, @a) = @_;
  433. my $name = $hash->{NAME};
  434. Log( 4, "SB_SERVER_Set($name): called" );
  435. if( @a < 2 ) {
  436. return( "at least one parameter is needed" ) ;
  437. }
  438. $name = shift( @a );
  439. my $cmd = shift( @a );
  440. if( $cmd eq "?" ) {
  441. # this one should give us a drop down list
  442. my $res = "Unknown argument ?, choose one of " .
  443. "on renew:noArg abort:noArg cliraw statusRequest:noArg ";
  444. $res .= "rescan:full,playlists ";
  445. #$res .= "addToFHEMUpdate:noArg removeFromFHEMUpdate:noArg"; # CD 0019
  446. return( $res );
  447. } elsif( $cmd eq "on" ) {
  448. if( ReadingsVal( $name, "power", "off" ) eq "off" ) {
  449. # the server is off, try to reactivate it
  450. if( $hash->{WOLNAME} ne "none" ) {
  451. fhem( "set $hash->{WOLNAME} on" );
  452. $hash->{helper}{WOLFastReconnectUntil}=time()+120; # CD 0007
  453. $hash->{helper}{WOLFastReconnectNext}=time()+30; # CD 0007
  454. }
  455. if( $hash->{RCCNAME} ne "none" ) {
  456. fhem( "set $hash->{RCCNAME} on" );
  457. }
  458. }
  459. } elsif( $cmd eq "renew" ) {
  460. Log3( $hash, 5, "SB_SERVER_Set: renew" );
  461. DevIo_SimpleWrite( $hash, "listen 1\n", 0 );
  462. } elsif( $cmd eq "abort" ) {
  463. DevIo_SimpleWrite( $hash, "listen 0\n", 0 );
  464. } elsif( $cmd eq "statusRequest" ) {
  465. Log3( $hash, 5, "SB_SERVER_Set: statusRequest" );
  466. DevIo_SimpleWrite( $hash, "version ?\n", 0 );
  467. DevIo_SimpleWrite( $hash, "serverstatus 0 200\n", 0 );
  468. DevIo_SimpleWrite( $hash, "favorites items 0 " .
  469. AttrVal( $name, "maxfavorites", 100 ) . " want_url:1\n", # CD 0009 url mit abfragen
  470. 0 );
  471. DevIo_SimpleWrite( $hash, "playlists 0 200\n", 0 );
  472. DevIo_SimpleWrite( $hash, "alarm playlists 0 300\n", 0 ); # CD 0011
  473. } elsif( $cmd eq "cliraw" ) {
  474. # write raw messages to the CLI interface per player
  475. my $v = join( " ", @a );
  476. $v .= "\n";
  477. Log3( $hash, 5, "SB_SERVER_Set: cliraw: $v " );
  478. DevIo_SimpleWrite( $hash, $v, 0 ); # CD 0016 IOWrite in DevIo_SimpleWrite geändert
  479. } elsif( $cmd eq "rescan" ) {
  480. DevIo_SimpleWrite( $hash, $cmd . " " . $a[ 0 ] . "\n", 0 ); # CD 0016 IOWrite in DevIo_SimpleWrite geändert
  481. # CD 0018 start
  482. #} elsif( $cmd eq "addToFHEMUpdate" ) {
  483. # fhem("update add https://raw.githubusercontent.com/ChrisD70/FHEM-Modules/master/autoupdate/sb/controls_squeezebox.txt");
  484. #} elsif( $cmd eq "removeFromFHEMUpdate" ) {
  485. # fhem("update delete https://raw.githubusercontent.com/ChrisD70/FHEM-Modules/master/autoupdate/sb/controls_squeezebox.txt");
  486. # CD 0018 end
  487. } else {
  488. ;
  489. }
  490. return( undef );
  491. }
  492. # ----------------------------------------------------------------------------
  493. # Read
  494. # called from the global loop, when the select for hash->{FD} reports data
  495. # ----------------------------------------------------------------------------
  496. sub SB_SERVER_Read( $ ) {
  497. my ($hash) = @_;
  498. my $name = $hash->{NAME};
  499. #my $start = time; # CD 0019
  500. Log3( $hash, 4, "SB_SERVER_Read($name): called" );
  501. Log3( $hash, 5, "+++++++++++++++++++++++++++++++++++++++++++++++++++++" );
  502. Log3( $hash, 5, "New Squeezebox Server Read cycle starts here" );
  503. Log3( $hash, 5, "+++++++++++++++++++++++++++++++++++++++++++++++++++++" );
  504. my $buf = DevIo_SimpleRead( $hash );
  505. if( !defined( $buf ) ) {
  506. return( "" );
  507. }
  508. # if we have data, the server is on again
  509. if( ReadingsVal( $name, "power", "off" ) ne "on" ) {
  510. readingsSingleUpdate( $hash, "power", "on", 1 );
  511. if( defined( $SB_SERVER_CmdStack{$name}{cnt} ) ) {
  512. my $maxmsg = $SB_SERVER_CmdStack{$name}{cnt};
  513. my $out;
  514. for( my $n = 0; $n <= $maxmsg; $n++ ) {
  515. $out = SB_SERVER_CMDStackPop( $hash );
  516. if( $out ne "empty" ) {
  517. DevIo_SimpleWrite( $hash, $out , 0 );
  518. }
  519. }
  520. }
  521. #Log3( $hash, 5, "SB_SERVER_Read($name): please implement the " . # CD 0009 Meldung deaktiviert
  522. # "sending of the CMDStack." ); # CD 0009 Meldung deaktiviert
  523. }
  524. #my $t1 = time; # CD 0020
  525. # if there are remains from the last time, append them now
  526. $buf = $hash->{PARTIAL} . $buf;
  527. $buf = uri_unescape( $buf );
  528. Log3( $hash, 6, "SB_SERVER_Read: the buf: $buf" ); # CD TEST 6
  529. # CD 0021 start - Server lebt noch, alivetimer neu starten
  530. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  531. InternalTimer( gettimeofday() +
  532. AttrVal( $name, "alivetimer", 10 ),
  533. "SB_SERVER_tcb_Alive",
  534. "SB_SERVER_Alive:$name",
  535. 0 );
  536. # CD 0021 end
  537. #my $t2 = time; # CD 0020
  538. # if we have received multiline commands, they are split by \n
  539. my @cmds = split( "\n", $buf );
  540. # check for last element in string
  541. my $lastchr = substr( $buf, -1, 1 );
  542. if( $lastchr ne "\n" ) {
  543. #ups, the return doesn't seem to be complete
  544. $hash->{PARTIAL} = $cmds[ $#cmds ];
  545. # and remove the last element
  546. pop( @cmds );
  547. Log3( $hash, 5, "SB_SERVER_Read: uncomplete command received" );
  548. } else {
  549. Log3( $hash, 5, "SB_SERVER_Read: complete command received" );
  550. $hash->{PARTIAL} = "";
  551. }
  552. #my $t3 = time; # CD 0020
  553. # and dispatch the rest
  554. foreach( @cmds ) {
  555. #my $t31=time; # CD 0020
  556. # double check complete line
  557. my $lastchar = substr( $_, -1);
  558. SB_SERVER_DispatchCommandLine( $hash, $_ );
  559. # CD 0020 start
  560. #if((time-$t31)>0.3) {
  561. # Log3($hash,0,"SB_SERVER_Read($name), time:".int((time-$t31)*1000)." cmd: ".$_);
  562. #}
  563. # CD 0020 end
  564. }
  565. #my $t4 = time; # CD 0020
  566. # CD 0009 check for reload of newer version
  567. $hash->{helper}{SB_SERVER_VERSION}=0 if (!defined($hash->{helper}{SB_SERVER_VERSION})); # CD 0012
  568. if ($hash->{helper}{SB_SERVER_VERSION} ne SB_SERVER_VERSION)
  569. {
  570. Log3( $hash, 1,"SB_SERVER_Read: SB_SERVER_VERSION changed from ".$hash->{helper}{SB_SERVER_VERSION}." to ".SB_SERVER_VERSION); # CD 0012
  571. $hash->{helper}{SB_SERVER_VERSION}=SB_SERVER_VERSION;
  572. DevIo_SimpleWrite( $hash, "version ?\n", 0 );
  573. DevIo_SimpleWrite( $hash, "serverstatus 0 200\n", 0 );
  574. DevIo_SimpleWrite( $hash, "favorites items 0 " .
  575. AttrVal( $name, "maxfavorites", 100 ) . " want_url:1\n", # CD 0009 url mit abfragen
  576. 0 );
  577. DevIo_SimpleWrite( $hash, "playlists 0 200\n", 0 );
  578. }
  579. # CD 0009 end
  580. Log3( $hash, 5, "+++++++++++++++++++++++++++++++++++++++++++++++++++++" );
  581. Log3( $hash, 5, "Squeezebox Server Read cycle ends here" );
  582. Log3( $hash, 5, "+++++++++++++++++++++++++++++++++++++++++++++++++++++" );
  583. # CD 0019 start
  584. #my $end = time;
  585. #if (($end - $start)>1) {
  586. # Log3( $hash, 0, "SB_SERVER_Read($name), times: ".int(($t1 - $start)*1000)." ".int(($t2 - $t1)*1000)." ".int(($t3 - $t2)*1000)." ".int(($t4 - $t3)*1000)." ".int(($end - $start)*1000)." nCmds: ".$#cmds );
  587. #}
  588. # CD 0019 end
  589. return( undef );
  590. }
  591. # ----------------------------------------------------------------------------
  592. # called by the clients to send data
  593. # ----------------------------------------------------------------------------
  594. sub SB_SERVER_Write( $$$ ) {
  595. my ( $hash, $fn, $msg ) = @_;
  596. my $name = $hash->{NAME};
  597. Log3( $hash, 4, "SB_SERVER_Write($name): called with FN:$fn" ); # unless($fn=~m/\?/); # CD TEST 4
  598. if( !defined( $fn ) ) {
  599. return( undef );
  600. }
  601. if( defined( $msg ) ) {
  602. Log3( $hash, 4, "SB_SERVER_Write: MSG:$msg" );
  603. }
  604. # CD 0012 fhemrelay Meldungen nicht an den LMS schicken sondern direkt an Dispatch übergeben
  605. if($fn =~ m/fhemrelay/) {
  606. SB_SERVER_DispatchCommandLine( $hash, $fn );
  607. return( undef );
  608. }
  609. if( ReadingsVal( $name, "serversecure", "0" ) eq "1" ) {
  610. if( ( $hash->{USERNAME} ne "?" ) && ( $hash->{PASSWORD} ne "?" ) ) {
  611. # we need to send username and password first
  612. } else {
  613. my $retmsg = "SB_SERVER_Write: Server needs username and " .
  614. "password but you did not specify those. No sending";
  615. Log3( $hash, 1, $retmsg );
  616. return( $retmsg );
  617. }
  618. }
  619. if( ReadingsVal( $name, "power", "on" ) eq "on" ) {
  620. DevIo_SimpleWrite( $hash, "$fn", 0 );
  621. } else {
  622. # we are off, so save the command for later
  623. # if maxcmdstack is 0, the function is turned off
  624. if( AttrVal( $name, "maxcmdstack", 100 ) > 0 ) {
  625. SB_SERVER_CMDStackPush( $hash, $fn );
  626. }
  627. }
  628. }
  629. # ----------------------------------------------------------------------------
  630. # Initialisation of the CLI connection
  631. # ----------------------------------------------------------------------------
  632. sub SB_SERVER_DoInit( $ ) {
  633. my ($hash) = @_;
  634. my $name = $hash->{NAME};
  635. Log3( $hash, 4, "SB_SERVER_DoInit($name): called" );
  636. if( !$hash->{TCPDev} ) {
  637. Log3( $hash, 2, "SB_SERVER_DoInit: no TCPDev available?" ); # CD 0009 level 5->2
  638. DevIo_CloseDev( $hash );
  639. }
  640. Log3( $hash, 3, "SB_SERVER_DoInit($name): STATE: " . $hash->{STATE} . " power: ". ReadingsVal( $name, "power", "X" )); # CD 0009 level 2 -> 3
  641. if( $hash->{STATE} eq "disconnected" ) {
  642. # server is off after FHEM start, broadcast to clients
  643. if( ( ReadingsVal( $name, "power", "on" ) eq "on" ) ||
  644. ( ReadingsVal( $name, "power", "on" ) eq "?" ) ) {
  645. Log3( $hash, 3, "SB_SERVER_DoInit($name): " . # CD 0009 level 2 -> 3
  646. "SB-Server in hibernate / suspend?." );
  647. # obviously the first we realize the Server is off
  648. readingsSingleUpdate( $hash, "power", "off", 1 );
  649. # and signal to our clients
  650. SB_SERVER_Broadcast( $hash, "SERVER", "OFF" );
  651. SB_SERVER_Broadcast( $hash, "SERVER",
  652. "IP " . $hash->{IP} . ":" .
  653. AttrVal( $name, "httpport", "9000" ) );
  654. }
  655. return( 1 );
  656. } elsif( $hash->{STATE} eq "opened" ) {
  657. $hash->{ALIVECHECK} = "?";
  658. $hash->{CLICONNECTION} = "on";
  659. if( ( ReadingsVal( $name, "power", "on" ) eq "off" ) ||
  660. ( ReadingsVal( $name, "power", "on" ) eq "?" ) ) {
  661. Log3( $hash, 3, "SB_SERVER_DoInit($name): " . # CD 0009 level 2 -> 3
  662. "SB-Server is back again." );
  663. # CD 0007 cleanup
  664. if(defined($hash->{helper}{WOLFastReconnectUntil})) {
  665. delete($hash->{TIMEOUT});
  666. delete($hash->{helper}{WOLFastReconnectUntil});
  667. delete($hash->{helper}{WOLFastReconnectNext});
  668. }
  669. $hash->{helper}{pingCounter}=0; # CD 0007
  670. SB_SERVER_Broadcast( $hash, "SERVER",
  671. "IP " . $hash->{IP} . ":" .
  672. AttrVal( $name, "httpport", "9000" ) );
  673. $hash->{helper}{doBroadcast}=1; # CD 0007
  674. SB_SERVER_LMS_Status( $hash );
  675. if( AttrVal( $name, "doalivecheck", "false" ) eq "false" ) {
  676. readingsSingleUpdate( $hash, "power", "on", 1 );
  677. #SB_SERVER_Broadcast( $hash, "SERVER", "ON" ); # CD 0007
  678. return( 0 );
  679. } elsif( AttrVal( $name, "doalivecheck", "false" ) eq "true" ) {
  680. # start the alive checking mechanism
  681. # CD 0020 SB_SERVER_tcb_Alive verwenden
  682. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  683. InternalTimer( gettimeofday() +
  684. AttrVal( $name, "alivetimer", 10 ),
  685. "SB_SERVER_tcb_Alive",
  686. "SB_SERVER_Alive:$name",
  687. 0 );
  688. return( 0 );
  689. } else {
  690. Log3( $hash, 2, "SB_SERVER_DoInit: doalivecheck has " .
  691. "wrong value" );
  692. return( 1 );
  693. }
  694. }
  695. } else {
  696. # what the f...
  697. Log3( $hash, 2, "SB_SERVER_DoInit: unclear status reported" );
  698. return( 1 );
  699. }
  700. #Log3( $hash, 3, "SB_SERVER_DoInit: something went wrong!" ); # CD 0008 nur für Testzwecke 0009 deaktiviert
  701. #return(0); # CD 0008 nur für Testzwecke 0009 deaktiviert
  702. return( 1 );
  703. }
  704. # ----------------------------------------------------------------------------
  705. # Dispatch every single line of commands
  706. # ----------------------------------------------------------------------------
  707. sub SB_SERVER_DispatchCommandLine( $$ ) {
  708. my ( $hash, $buf ) = @_;
  709. my $name = $hash->{NAME};
  710. Log3( $hash, 4, "SB_SERVER_DispatchCommandLine($name): Line:$buf..." );
  711. # try to extract the first answer to the SPACE
  712. my $indx = index( $buf, " " );
  713. my $id1 = substr( $buf, 0, $indx );
  714. # is the first return value a player ID?
  715. # Player ID is MAC adress, hence : included
  716. my @id = split( ":", $id1 );
  717. if( @id > 1 ) {
  718. # we have received a return for a dedicated player
  719. # create the fhem specific unique id
  720. my $playerid = join( "", @id );
  721. Log3( $hash, 5, "SB_SERVER_DispatchCommandLine: fhem-id: $playerid" );
  722. # create the commands
  723. my $cmds = substr( $buf, $indx + 1 );
  724. Log3( $hash, 5, "SB_SERVER__DispatchCommandLine: commands: $cmds" );
  725. Dispatch( $hash, "SB_PLAYER:$playerid:$cmds", undef );
  726. } else {
  727. # that is a server specific command
  728. SB_SERVER_ParseCmds( $hash, $buf );
  729. }
  730. return( undef );
  731. }
  732. # ----------------------------------------------------------------------------
  733. # parse the server answers that are not intended for players
  734. # ----------------------------------------------------------------------------
  735. sub SB_SERVER_ParseCmds( $$ ) {
  736. my ( $hash, $instr ) = @_;
  737. my $name = $hash->{NAME};
  738. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): called" );
  739. my @args = split( " ", $instr );
  740. $hash->{LASTANSWER} = "@args";
  741. my $cmd = shift( @args );
  742. # CD 0007 start
  743. if (defined($hash->{helper}{doBroadcast})) {
  744. SB_SERVER_Broadcast( $hash, "SERVER", "ON" );
  745. SB_SERVER_Broadcast( $hash, "SERVER",
  746. "IP " . $hash->{IP} . ":" .
  747. AttrVal( $name, "httpport", "9000" ) );
  748. delete ($hash->{helper}{doBroadcast});
  749. }
  750. # CD 0007 end
  751. if( $cmd eq "version" ) {
  752. readingsSingleUpdate( $hash, "serverversion", $args[ 1 ], 0 );
  753. if( ReadingsVal( $name, "power", "off" ) eq "off" ) {
  754. # that also means the server returned from being away
  755. readingsSingleUpdate( $hash, "power", "on", 1 );
  756. # signal our players
  757. SB_SERVER_Broadcast( $hash, "SERVER", "ON" );
  758. SB_SERVER_Broadcast( $hash, "SERVER",
  759. "IP " . $hash->{IP} . ":" .
  760. AttrVal( $name, "httpport", "9000" ) );
  761. }
  762. } elsif( $cmd eq "pref" ) {
  763. if( $args[ 0 ] eq "authorize" ) {
  764. readingsSingleUpdate( $hash, "serversecure", $args[ 1 ], 0 );
  765. if( $args[ 1 ] eq "1" ) {
  766. # username and password is required
  767. # CD 0007 zu spät, login muss als erstes gesendet werden, andernfalls bricht der Server die Verbindung sofort ab
  768. if( ( $hash->{USERNAME} ne "?" ) &&
  769. ( $hash->{PASSWORD} ne "?" ) ) {
  770. DevIo_SimpleWrite( $hash, "login " .
  771. $hash->{USERNAME} . " " .
  772. $hash->{PASSWORD} . "\n",
  773. 0 );
  774. } else {
  775. Log3( $hash, 3, "SB_SERVER_ParseCmds($name): login " .
  776. "required but no username and password specified" );
  777. }
  778. # next step is to wait for the answer of the LMS server
  779. } elsif( $args[ 1 ] eq "0" ) {
  780. # no username password required, go ahead directly
  781. #SB_SERVER_LMS_Status( $hash );
  782. } else {
  783. Log3( $hash, 3, "SB_SERVER_ParseCmds($name): unkown " .
  784. "result for authorize received. Should be 0 or 1" );
  785. }
  786. }
  787. } elsif( $cmd eq "login" ) {
  788. if( ( $args[ 1 ] eq $hash->{USERNAME} ) &&
  789. ( $args[ 2 ] eq "******" ) ) {
  790. # login has been succesful, go ahead
  791. SB_SERVER_LMS_Status( $hash );
  792. }
  793. } elsif( $cmd eq "fhemalivecheck" ) {
  794. $hash->{ALIVECHECK} = "received";
  795. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): alivecheck received" );
  796. } elsif( $cmd eq "favorites" ) {
  797. if( $args[ 0 ] eq "changed" ) {
  798. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): favorites changed" );
  799. # we need to trigger the favorites update here
  800. DevIo_SimpleWrite( $hash, "favorites items 0 " .
  801. AttrVal( $name, "maxfavorites", 100 ) .
  802. " want_url:1\n", 0 ); # CD 0009 url mit abfragen
  803. DevIo_SimpleWrite( $hash, "alarm playlists 0 300\n", 0 ); # CD 0011
  804. } elsif( $args[ 0 ] eq "items" ) {
  805. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): favorites items" );
  806. # the response to our query of the favorites
  807. SB_SERVER_FavoritesParse( $hash, join( " ", @args ) );
  808. } else {
  809. }
  810. } elsif( $cmd eq "serverstatus" ) {
  811. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): server status" );
  812. SB_SERVER_ParseServerStatus( $hash, \@args );
  813. } elsif( $cmd eq "playlists" ) {
  814. Log3( $hash, 4, "SB_SERVER_ParseCmds($name): playlists" );
  815. # CD 0004 Playlisten neu anfragen bei Änderung
  816. if(($args[0] eq "rename")||($args[0] eq "delete")) {
  817. DevIo_SimpleWrite( $hash, "playlists 0 200\n", 0 );
  818. DevIo_SimpleWrite( $hash, "alarm playlists 0 300\n", 0 ); # CD 0011
  819. } else {
  820. SB_SERVER_ParseServerPlaylists( $hash, \@args );
  821. }
  822. } elsif( $cmd eq "client" ) {
  823. # CD 0011 start
  824. } elsif( $cmd eq "alarm" ) {
  825. if( $args[0] eq "playlists" ) {
  826. SB_SERVER_ParseServerAlarmPlaylists( $hash, \@args );
  827. }
  828. # CD 0011 end
  829. # CD 0016 start
  830. } elsif( $cmd eq "rescan" ) {
  831. if( $args[0] eq "done" ) {
  832. DevIo_SimpleWrite( $hash, "serverstatus 0 200\n", 0 );
  833. }
  834. # CD 0016 end
  835. } else {
  836. # unkown
  837. }
  838. }
  839. # CD 0020 start
  840. sub SB_SERVER_tcb_Alive($) {
  841. my($in ) = shift;
  842. my(undef,$name) = split(':',$in);
  843. my $hash = $defs{$name};
  844. #Log 0,"SB_SERVER_tcb_Alive";
  845. SB_SERVER_Alive($hash);
  846. }
  847. # CD 0020 end
  848. # ----------------------------------------------------------------------------
  849. # Alivecheck of the server
  850. # ----------------------------------------------------------------------------
  851. sub SB_SERVER_Alive( $ ) {
  852. my ($hash) = @_;
  853. my $name = $hash->{NAME};
  854. # CD 0004 set default to off
  855. #my $rccstatus = "on";
  856. #my $pingstatus = "on";
  857. my $rccstatus = "off";
  858. my $pingstatus = "off";
  859. my $nexttime = gettimeofday() + AttrVal( $name, "alivetimer", 120 );
  860. Log3( $hash, 4, "SB_SERVER_Alive($name): called" ); # CD 0006 changed log level from 4 to 2 # CD 0009 level 2->3 # CD 0014 level -> 4
  861. if( AttrVal( $name, "doalivecheck", "false" ) eq "false" ) {
  862. Log3( $hash, 5, "SB_SERVER_Alive($name): alivechecking is off" );
  863. $rccstatus = "on";
  864. $pingstatus = "on";
  865. $hash->{helper}{pingCounter}=0; # CD 0004
  866. } else {
  867. # check via the RCC element
  868. if( $hash->{RCCNAME} ne "none" ) {
  869. # an RCC element has been given as argument
  870. $rccstatus = ReadingsVal( $hash->{RCCNAME}, "state", "off" );
  871. }
  872. # CD 0007 start
  873. if (($hash->{PRESENCENAME} ne "none")
  874. && defined($defs{$hash->{PRESENCENAME}})
  875. && defined($defs{$hash->{PRESENCENAME}}->{TIMEOUT_NORMAL})
  876. && (($defs{$hash->{PRESENCENAME}}->{TIMEOUT_NORMAL}) < AttrVal( $name, "alivetimer", 30 ))) {
  877. Log3( $hash, 4,"SB_SERVER_Alive($name): using $hash->{PRESENCENAME}"); # CD 0009 level 2->4
  878. if( ReadingsVal( $hash->{PRESENCENAME}, "state", "absent" ) eq "present" ) {
  879. $pingstatus = "on";
  880. $hash->{helper}{pingCounter}=0;
  881. } else {
  882. $pingstatus = "off";
  883. $hash->{helper}{pingCounter}=$hash->{helper}{pingCounter}+1;
  884. $nexttime = gettimeofday() + 15;
  885. }
  886. } else {
  887. # CD 0007 end
  888. # CD 0021 start
  889. my $ipp=AttrVal($name, "internalPingProtocol", "tcp" );
  890. if($ipp eq "none") {
  891. if ($hash->{STATE} eq "disconnected") {
  892. $pingstatus = "off";
  893. $hash->{helper}{pingCounter}=3;
  894. } else {
  895. $pingstatus = "on";
  896. $hash->{helper}{pingCounter}=0;
  897. }
  898. } else {
  899. # CD 0021 end
  900. Log3( $hash, 4,"SB_SERVER_Alive($name): using internal ping"); # CD 0007 # CD 0009 level 2->4
  901. # check via ping
  902. my $p;
  903. # CD 0017 eval hinzugefügt, Absturz auf FritzBox, bei Fehler annehmen dass Host verfügbar ist, internalPingProtocol hinzugefügt
  904. eval { $p = Net::Ping->new( $ipp ); };
  905. if($@) {
  906. Log3( $hash,1,"SB_SERVER_Alive($name): internal ping failed with $@");
  907. $pingstatus = "on";
  908. $hash->{helper}{pingCounter}=0;
  909. } else {
  910. if( $p->ping( $hash->{IP}, 2 ) ) {
  911. $pingstatus = "on";
  912. $hash->{helper}{pingCounter}=0; # CD 0004
  913. } else {
  914. $pingstatus = "off";
  915. $hash->{helper}{pingCounter}=$hash->{helper}{pingCounter}+1; # CD 0004
  916. }
  917. # close our ping mechanism again
  918. $p->close( );
  919. }
  920. } # CD 0021
  921. } # CD 0007
  922. Log3( $hash, 5, "SB_SERVER_Alive($name): " . # CD Test 5
  923. "RCC:" . $rccstatus . " Ping:" . $pingstatus ); # CD 0006 changed log level from 5 to 2 # CD 0009 level 2->3 # CD 0014 level -> 5
  924. }
  925. # set the status of the server accordingly
  926. # CD 0004 added sensitivity to ping
  927. # if( ( $rccstatus eq "on" ) || ( $pingstatus eq "on" ) ) {
  928. if( ( $rccstatus eq "on" ) || ( $hash->{helper}{pingCounter}<3 ) ) {
  929. # the server is reachable
  930. if( ReadingsVal( $name, "power", "on" ) eq "off" ) {
  931. # the first time we see the server being on
  932. Log3( $hash, 3, "SB_SERVER_Alive($name): " . # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
  933. "SB-Server is back again." );
  934. # first time we realized server is away
  935. if( $hash->{STATE} eq "disconnected" ) {
  936. delete($hash->{NEXT_OPEN}) if($hash->{NEXT_OPEN}); # CD 0007 remove delay for reconnect
  937. DevIo_OpenDev( $hash, 1, "SB_SERVER_DoInit" );
  938. }
  939. readingsSingleUpdate( $hash, "power", "on", 1 );
  940. $hash->{ALIVECHECK} = "?";
  941. $hash->{CLICONNECTION} = "off";
  942. # quicker update to capture CLI connection faster
  943. $nexttime = gettimeofday() + 10;
  944. } else { # CD 0005
  945. # check the CLI connection (sub-state)
  946. if( $hash->{ALIVECHECK} eq "waiting" ) {
  947. # ups, we did not receive any answer in the last minutes
  948. # SB Server potentially dead or shut-down
  949. Log3( $hash, 3, "SB_SERVER_Alive($name): overrun SB-Server dead." ); # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
  950. $hash->{CLICONNECTION} = "off";
  951. # signal that to our clients
  952. SB_SERVER_Broadcast( $hash, "SERVER", "OFF" );
  953. # close the device
  954. # CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
  955. #DevIo_CloseDev( $hash );
  956. DevIo_Disconnected( $hash );
  957. $hash->{helper}{pingCounter}=9999; # CD 0007
  958. # CD 0000 start - exit infinite loop after socket has been closed
  959. $hash->{ALIVECHECK} = "?";
  960. $hash->{STATE}="disconnected";
  961. # CD 0005 line above does not work (on Linux), fix:
  962. # CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
  963. SB_SERVER_setStates($hash, "disconnected");
  964. readingsSingleUpdate( $hash, "power", "off", 1 );
  965. # test: clear stack ?
  966. $SB_SERVER_CmdStack{$name}{last_n} = 0;
  967. $SB_SERVER_CmdStack{$name}{first_n} = 0;
  968. $SB_SERVER_CmdStack{$name}{cnt} = 0;
  969. # CD end
  970. # remove all timers we created
  971. RemoveInternalTimer( $hash );
  972. } else {
  973. if( $hash->{CLICONNECTION} eq "off" ) {
  974. # signal that to our clients
  975. # to be revisited, should only be sent after CLI established
  976. #SB_SERVER_Broadcast( $hash, "SERVER", "ON" ); # CD 0007 disabled, wait for SB_SERVER_LMS_Status
  977. SB_SERVER_LMS_Status( $hash );
  978. }
  979. $hash->{CLICONNECTION} = "on";
  980. # just send something to the SB-Server. It will echo it
  981. # if we receive the echo, the server is still alive
  982. $hash->{ALIVECHECK} = "waiting";
  983. DevIo_SimpleWrite( $hash, "fhemalivecheck\n", 0 );
  984. }
  985. }
  986. } elsif( ( $rccstatus eq "off" ) && ( $pingstatus eq "off" ) ) {
  987. if( ReadingsVal( $name, "power", "on" ) eq "on" ) {
  988. # the first time we realize the server is off
  989. Log3( $hash, 3, "SB_SERVER_Alive($name): " . # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
  990. "SB-Server in hibernate / suspend?." );
  991. # first time we realized server is away
  992. $hash->{CLICONNECTION} = "off";
  993. readingsSingleUpdate( $hash, "power", "off", 1 );
  994. $hash->{ALIVECHECK} = "?";
  995. # signal that to our clients
  996. SB_SERVER_Broadcast( $hash, "SERVER", "OFF" );
  997. # close the device
  998. # CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
  999. #DevIo_CloseDev( $hash );
  1000. DevIo_Disconnected( $hash );
  1001. $hash->{helper}{pingCounter}=9999; # CD 0007
  1002. # CD 0004 set STATE, needed for reconnect
  1003. $hash->{STATE}="disconnected";
  1004. # CD 0005 line above does not work (on Linux), fix:
  1005. # CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
  1006. SB_SERVER_setStates($hash, "disconnected");
  1007. # remove all timers we created
  1008. RemoveInternalTimer( $hash );
  1009. }
  1010. } else {
  1011. # we shouldn't end up here
  1012. Log3( $hash, 5, "SB_SERVER_Alive($name): funny server status " .
  1013. "received. Ping=" . $pingstatus . " RCC=" . $rccstatus );
  1014. }
  1015. # do an update of the status
  1016. # CD 0020 SB_SERVER_tcb_Alive verwenden
  1017. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  1018. InternalTimer( $nexttime,
  1019. "SB_SERVER_tcb_Alive",
  1020. "SB_SERVER_Alive:$name",
  1021. 0 );
  1022. }
  1023. # ----------------------------------------------------------------------------
  1024. # Broadcast a message to all clients
  1025. # ----------------------------------------------------------------------------
  1026. sub SB_SERVER_Broadcast( $$@ ) {
  1027. my( $hash, $cmd, $msg, $bin ) = @_;
  1028. my $name = $hash->{NAME};
  1029. my $iodevhash;
  1030. Log3( $hash, 4, "SB_SERVER_Broadcast($name): called with $cmd - $msg" );
  1031. if( !defined( $bin ) ) {
  1032. $bin = 0;
  1033. }
  1034. foreach my $mydev ( keys %defs ) {
  1035. # the hash to the IODev as defined at the client
  1036. if( defined( $defs{$mydev}{IODev} ) ) {
  1037. $iodevhash = $defs{$mydev}{IODev};
  1038. } else {
  1039. $iodevhash = undef;
  1040. }
  1041. if( defined( $iodevhash ) ) {
  1042. if( ( defined( $defs{$mydev}{TYPE} ) ) &&
  1043. ( defined( $iodevhash->{NAME} ) ) ){
  1044. if( ( $defs{$mydev}{TYPE} eq "SB_PLAYER" ) &&
  1045. ( $iodevhash->{NAME} eq $name ) ) {
  1046. # we found a valid entry
  1047. my $clienthash = $defs{$mydev};
  1048. my $namebuf = $clienthash->{NAME};
  1049. SB_PLAYER_RecBroadcast( $clienthash, $cmd, $msg, $bin );
  1050. }
  1051. }
  1052. }
  1053. }
  1054. return;
  1055. }
  1056. # ----------------------------------------------------------------------------
  1057. # Handle the return for a serverstatus query
  1058. # ----------------------------------------------------------------------------
  1059. sub SB_SERVER_ParseServerStatus( $$ ) {
  1060. my( $hash, $dataptr ) = @_;
  1061. my $name = $hash->{NAME};
  1062. Log3( $hash, 4, "SB_SERVER_ParseServerStatus($name): called " );
  1063. # typically the start index being a number
  1064. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  1065. shift( @{$dataptr} );
  1066. } else {
  1067. Log3( $hash, 5, "SB_SERVER_ParseServerStatus($name): entry is " .
  1068. "not the start number" );
  1069. return;
  1070. }
  1071. # typically the max index being a number
  1072. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  1073. shift( @{$dataptr} );
  1074. } else {
  1075. Log3( $hash, 5, "SB_SERVER_ParseServerStatus($name): entry is " .
  1076. "not the end number" );
  1077. return;
  1078. }
  1079. my $datastr = join( " ", @{$dataptr} );
  1080. # replace funny stuff
  1081. $datastr =~ s/info total albums/infototalalbums/g;
  1082. $datastr =~ s/info total artists/infototalartists/g;
  1083. $datastr =~ s/info total songs/infototalsongs/g;
  1084. $datastr =~ s/info total genres/infototalgenres/g;
  1085. $datastr =~ s/sn player count/snplayercount/g;
  1086. $datastr =~ s/other player count/otherplayercount/g;
  1087. $datastr =~ s/player count/playercount/g;
  1088. Log3( $hash, 5, "SB_SERVER_ParseServerStatus($name): data to parse: " .
  1089. $datastr );
  1090. my @data1 = split( " ", $datastr );
  1091. # the rest of the array should now have the data, we're interested in
  1092. readingsBeginUpdate( $hash );
  1093. # set default values for stuff not always send
  1094. readingsBulkUpdate( $hash, "scanning", "no" );
  1095. readingsBulkUpdate( $hash, "scandb", "?" );
  1096. readingsBulkUpdate( $hash, "scanprogressdone", "0" );
  1097. readingsBulkUpdate( $hash, "scanprogresstotal", "0" );
  1098. readingsBulkUpdate( $hash, "scanlastfailed", "none" );
  1099. my $addplayers = true;
  1100. my %players;
  1101. my $currentplayerid = "none";
  1102. # needed for scanning the MAC Adress
  1103. my $d = "[0-9A-Fa-f]";
  1104. my $dd = "$d$d";
  1105. # needed for scanning the IP adress
  1106. my $e = "[0-9]";
  1107. my $ee = "$e$e";
  1108. foreach( @data1 ) {
  1109. if( $_ =~ /^(lastscan:)([0-9]*)/ ) {
  1110. # we found the lastscan entry
  1111. my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
  1112. localtime( $2 );
  1113. $year = $year + 1900;
  1114. readingsBulkUpdate( $hash, "scan_last", "$mday-".($mon+1)."-$year " . # CD 0016 Monat korrigiert
  1115. "$hour:$min:$sec" );
  1116. next;
  1117. } elsif( $_ =~ /^(scanning:)([0-9]*)/ ) {
  1118. readingsBulkUpdate( $hash, "scanning", $2 );
  1119. next;
  1120. } elsif( $_ =~ /^(rescan:)([0-9]*)/ ) {
  1121. if( $2 eq "1" ) {
  1122. readingsBulkUpdate( $hash, "scanning", "yes" );
  1123. } else {
  1124. readingsBulkUpdate( $hash, "scanning", "no" );
  1125. }
  1126. next;
  1127. } elsif( $_ =~ /^(version:)([0-9\.]*)/ ) {
  1128. readingsBulkUpdate( $hash, "serverversion", $2 );
  1129. next;
  1130. } elsif( $_ =~ /^(playercount:)([0-9]*)/ ) {
  1131. readingsBulkUpdate( $hash, "players", $2 );
  1132. next;
  1133. } elsif( $_ =~ /^(snplayercount:)([0-9]*)/ ) {
  1134. readingsBulkUpdate( $hash, "players_mysb", $2 );
  1135. $currentplayerid = "none";
  1136. $addplayers = false;
  1137. next;
  1138. } elsif( $_ =~ /^(otherplayercount:)([0-9]*)/ ) {
  1139. readingsBulkUpdate( $hash, "players_other", $2 );
  1140. $currentplayerid = "none";
  1141. $addplayers = false;
  1142. next;
  1143. } elsif( $_ =~ /^(infototalalbums:)([0-9]*)/ ) {
  1144. readingsBulkUpdate( $hash, "db_albums", $2 );
  1145. next;
  1146. } elsif( $_ =~ /^(infototalartists:)([0-9]*)/ ) {
  1147. readingsBulkUpdate( $hash, "db_artists", $2 );
  1148. next;
  1149. } elsif( $_ =~ /^(infototalsongs:)([0-9]*)/ ) {
  1150. readingsBulkUpdate( $hash, "db_songs", $2 );
  1151. next;
  1152. } elsif( $_ =~ /^(infototalgenres:)([0-9]*)/ ) {
  1153. readingsBulkUpdate( $hash, "db_genres", $2 );
  1154. next;
  1155. } elsif( $_ =~ /^(playerid:)($dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd)/ ) {
  1156. my $id = join( "", split( ":", $2 ) );
  1157. if( $addplayers == true ) { # CD 0017 fixed ==
  1158. $players{$id}{ID} = $id;
  1159. $players{$id}{MAC} = $2;
  1160. $currentplayerid = $id;
  1161. }
  1162. next;
  1163. } elsif( $_ =~ /^(name:)(.*)/ ) {
  1164. if( $currentplayerid ne "none" ) {
  1165. $players{$currentplayerid}{name} = $2;
  1166. }
  1167. next;
  1168. } elsif( $_ =~ /^(displaytype:)(.*)/ ) {
  1169. if( $currentplayerid ne "none" ) {
  1170. $players{$currentplayerid}{displaytype} = $2;
  1171. }
  1172. next;
  1173. } elsif( $_ =~ /^(model:)(.*)/ ) {
  1174. if( $currentplayerid ne "none" ) {
  1175. $players{$currentplayerid}{model} = $2;
  1176. }
  1177. next;
  1178. } elsif( $_ =~ /^(power:)([0|1])/ ) {
  1179. if( $currentplayerid ne "none" ) {
  1180. $players{$currentplayerid}{power} = $2;
  1181. }
  1182. next;
  1183. } elsif( $_ =~ /^(canpoweroff:)([0|1])/ ) {
  1184. if( $currentplayerid ne "none" ) {
  1185. $players{$currentplayerid}{canpoweroff} = $2;
  1186. }
  1187. next;
  1188. } elsif( $_ =~ /^(connected:)([0|1])/ ) {
  1189. if( $currentplayerid ne "none" ) {
  1190. $players{$currentplayerid}{connected} = $2;
  1191. }
  1192. next;
  1193. } elsif( $_ =~ /^(isplayer:)([0|1])/ ) {
  1194. if( $currentplayerid ne "none" ) {
  1195. $players{$currentplayerid}{isplayer} = $2;
  1196. }
  1197. next;
  1198. } elsif( $_ =~ /^(ip:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{3,5})/ ) {
  1199. if( $currentplayerid ne "none" ) {
  1200. $players{$currentplayerid}{IP} = $2;
  1201. }
  1202. next;
  1203. } elsif( $_ =~ /^(seq_no:)(.*)/ ) {
  1204. # just to take care of the keyword
  1205. next;
  1206. # CD 0017 start
  1207. } elsif( $_ =~ /^(isplaying:)(.*)/ ) {
  1208. # just to take care of the keyword
  1209. next;
  1210. } elsif( $_ =~ /^(snplayercount:)(.*)/ ) {
  1211. # just to take care of the keyword
  1212. next;
  1213. } elsif( $_ =~ /^(otherplayercount:)(.*)/ ) {
  1214. # just to take care of the keyword
  1215. next;
  1216. } elsif( $_ =~ /^(server:)(.*)/ ) {
  1217. # just to take care of the keyword
  1218. next;
  1219. } elsif( $_ =~ /^(serverurl:)(.*)/ ) {
  1220. # just to take care of the keyword
  1221. next;
  1222. # CD 0017 end
  1223. } else {
  1224. # no keyword found, so let us assume it is part of the player name
  1225. if( $currentplayerid ne "none" ) {
  1226. $players{$currentplayerid}{name} .= $_;
  1227. }
  1228. }
  1229. }
  1230. readingsEndUpdate( $hash, 1 );
  1231. my @ignoredIPs=split(',',AttrVal($name,'ignoredIPs','')); # CD 0017
  1232. my @ignoredMACs=split(',',AttrVal($name,'ignoredMACs','')); # CD 0017
  1233. foreach my $player ( keys %players ) {
  1234. if( defined( $players{$player}{isplayer} ) ) {
  1235. if( $players{$player}{isplayer} eq "0" ) {
  1236. Log3( $hash, 1, "not a player" );
  1237. next;
  1238. }
  1239. }
  1240. # CD 0017 check ignored IPs
  1241. if( defined( $players{$player}{IP} ) ) {
  1242. my @ip=split(':',$players{$player}{IP});
  1243. if ($ip[0] ~~ @ignoredIPs) {
  1244. $players{$player}{ignore}=1;
  1245. next;
  1246. }
  1247. }
  1248. # CD 0017 check ignored MACs
  1249. if( defined( $players{$player}{MAC} ) ) {
  1250. if ($players{$player}{MAC} ~~ @ignoredMACs) {
  1251. $players{$player}{ignore}=1;
  1252. next;
  1253. }
  1254. }
  1255. # if the player is not yet known, it will be created
  1256. if( defined( $players{$player}{ID} ) ) {
  1257. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:NONE", undef );
  1258. } else {
  1259. Log3( $hash, 1, "not defined" );
  1260. next;
  1261. }
  1262. if( defined( $players{$player}{name} ) ) {
  1263. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1264. "name $players{$player}{name}", undef );
  1265. }
  1266. if( defined( $players{$player}{IP} ) ) {
  1267. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1268. "player ip $players{$player}{IP}", undef );
  1269. }
  1270. if( defined( $players{$player}{model} ) ) {
  1271. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1272. "player model $players{$player}{model}", undef );
  1273. }
  1274. if( defined( $players{$player}{canpoweroff} ) ) {
  1275. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1276. "player canpoweroff $players{$player}{canpoweroff}",
  1277. undef );
  1278. }
  1279. if( defined( $players{$player}{power} ) ) {
  1280. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1281. "power $players{$player}{power}", undef );
  1282. }
  1283. if( defined( $players{$player}{connected} ) ) {
  1284. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1285. "connected $players{$player}{connected}", undef );
  1286. }
  1287. if( defined( $players{$player}{displaytype} ) ) {
  1288. Dispatch( $hash, "SB_PLAYER:$players{$player}{ID}:" .
  1289. "displaytype $players{$player}{displaytype}", undef );
  1290. }
  1291. }
  1292. # the list for the sync masters
  1293. # make all client create e new sync master list
  1294. SB_SERVER_Broadcast( $hash, "SYNCMASTER",
  1295. "FLUSH dont care", undef );
  1296. # now send the list for the sync masters
  1297. foreach my $player ( keys %players ) {
  1298. next if defined($players{$player}{ignore});
  1299. my $uniqueid = join( "", split( ":", $players{$player}{MAC} ) );
  1300. Log3( $hash, 1, "SB_SERVER_ParseServerStatus($name): player has no name") unless defined($players{$player}{name});
  1301. Log3( $hash, 1, "SB_SERVER_ParseServerStatus($name): player has no MAC") unless defined($players{$player}{MAC});
  1302. SB_SERVER_Broadcast( $hash, "SYNCMASTER",
  1303. "ADD $players{$player}{name} " .
  1304. "$players{$player}{MAC} $uniqueid", undef );
  1305. }
  1306. return;
  1307. }
  1308. # ----------------------------------------------------------------------------
  1309. # Parse the return values of the favorites items
  1310. # ----------------------------------------------------------------------------
  1311. sub SB_SERVER_FavoritesParse( $$ ) {
  1312. my ( $hash, $str ) = @_;
  1313. my $name = $hash->{NAME};
  1314. Log3( $hash, 5, "SB_SERVER_FavoritesParse($name): called" );
  1315. # flush the existing list
  1316. foreach my $titi ( keys %{$favorites{$name}} ) {
  1317. delete( $favorites{$name}{$titi} );
  1318. }
  1319. # split up the string we got
  1320. my @data = split( " ", $str );
  1321. # eliminate the first entries of the response
  1322. # some more comment
  1323. # typically 'items'
  1324. if( $data[ 0 ] =~ /^(items)*/ ) {
  1325. my $notneeded = shift( @data );
  1326. }
  1327. # typically the start index being a number
  1328. if( $data[ 0 ] =~ /^([0-9])*/ ) {
  1329. my $notneeded = shift( @data );
  1330. }
  1331. # typically the start index being a number
  1332. my $maxwanted = 100;
  1333. if( $data[ 0 ] =~ /^([0-9])*/ ) {
  1334. $maxwanted = int( shift( @data ) );
  1335. }
  1336. # find the maximum number of favorites. That is typically at the
  1337. # end of the server response. So check there first
  1338. my $totals = 0;
  1339. my $lastdata = $data[ $#data ];
  1340. if( $lastdata =~ /^(count:)([0-9]*)/ ) {
  1341. $totals = $2;
  1342. # remove the last element from the array
  1343. pop( @data );
  1344. } else {
  1345. my $i = 0;
  1346. my $delneeded = false;
  1347. foreach( @data ) {
  1348. if( $_ =~ /^(count:)([0-9]*)/ ) {
  1349. $totals = $2;
  1350. $delneeded = true;
  1351. last;
  1352. } else {
  1353. $i++;
  1354. }
  1355. # delete the element from the list
  1356. if( $delneeded == true ) {
  1357. splice( @data, $i, 1 );
  1358. }
  1359. }
  1360. }
  1361. readingsSingleUpdate( $hash, "favoritestotal", $totals, 0 );
  1362. my $favname = "";
  1363. if( $data[ 0 ] =~ /^(title:)(.*)/ ) {
  1364. $favname = $2;
  1365. shift( @data );
  1366. }
  1367. readingsSingleUpdate( $hash, "favoritesname", $favname, 0 );
  1368. # check if we got all the favoites with our response
  1369. if( $totals > $maxwanted ) {
  1370. # we asked for too less data, there are more favorites defined
  1371. }
  1372. # treat the rest of the string
  1373. my $namestarted = false;
  1374. my $firstone = true;
  1375. my $namebuf = "";
  1376. my $idbuf = "";
  1377. my $hasitemsbuf = false;
  1378. my $isaudiobuf = "";
  1379. my $isplaylist = false;
  1380. my $url = "?"; # CD 0009 hinzugefügt
  1381. foreach ( @data ) {
  1382. #Log 0,$_;
  1383. if( $_ =~ /^(id:|ID:)([A-Za-z0-9\.]*)/ ) {
  1384. # we found an ID, that is typically the start of a new session
  1385. # so save the old session first
  1386. if( $firstone == false ) {
  1387. if(( $hasitemsbuf == false )||($isplaylist == true)) {
  1388. # derive our hash entry
  1389. my $entryuid = SB_SERVER_FavoritesName2UID( $namebuf ); # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
  1390. $favorites{$name}{$entryuid} = {
  1391. ID => $idbuf,
  1392. Name => $namebuf,
  1393. URL => $url, }; # CD 0009 hinzugefügt
  1394. $namebuf = "";
  1395. $isaudiobuf = "";
  1396. $url = "?"; # CD 0009 hinzugefügt
  1397. $hasitemsbuf = false;
  1398. $isplaylist = false;
  1399. } else {
  1400. # that is a folder we found, but we don't handle that
  1401. }
  1402. }
  1403. $firstone = false;
  1404. $idbuf = $2;
  1405. # if there has been a name found before, end it now
  1406. if( $namestarted == true ) {
  1407. $namestarted = false;
  1408. }
  1409. } elsif( $_ =~ /^(isaudio:)([0|1]?)/ ) {
  1410. $isaudiobuf = $2;
  1411. if( $namestarted == true ) {
  1412. $namestarted = false;
  1413. }
  1414. } elsif( $_ =~ /^(hasitems:)([0|1]?)/ ) {
  1415. if( int( $2 ) == 0 ) {
  1416. $hasitemsbuf = false;
  1417. } else {
  1418. $hasitemsbuf = true;
  1419. }
  1420. if( $namestarted == true ) {
  1421. $namestarted = false;
  1422. }
  1423. # CD 0018 start
  1424. } elsif( $_ =~ /^(type:)(.*)/ ) {
  1425. $isplaylist = true if($2 eq "playlist");
  1426. if( $namestarted == true ) {
  1427. $namestarted = false;
  1428. }
  1429. # CD 0018 end
  1430. #} elsif( $_ =~ /^(name:)([0-9a-zA-Z]*)/ ) { # CD 0007 # CD 0009 deaktiviert
  1431. } elsif( $_ =~ /^(name:)(.*)/ ) { # CD 0009 hinzugefügt
  1432. $namebuf = $2;
  1433. $namestarted = true;
  1434. # CD 0009 start
  1435. } elsif( $_ =~ /^(url:)(.*)/ ) {
  1436. $url = $2;
  1437. $url =~ s/file:\/\/\///;
  1438. # CD 0009 end
  1439. } else {
  1440. # no regexp matched, so it must be part of the name
  1441. if( $namestarted == true ) {
  1442. $namebuf .= " " . $_;
  1443. }
  1444. }
  1445. }
  1446. # capture the last element also
  1447. if( ( $namebuf ne "" ) && ( $idbuf ne "" ) ) {
  1448. if(( $hasitemsbuf == false )||($isplaylist == true)) {
  1449. # CD 0003 replaced ** my $entryuid = join( "", split( " ", $namebuf ) ); ** with:
  1450. my $entryuid = SB_SERVER_FavoritesName2UID( $namebuf ); # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
  1451. $favorites{$name}{$entryuid} = {
  1452. ID => $idbuf,
  1453. Name => $namebuf,
  1454. URL => $url, }; # CD 0009 hinzugefügt
  1455. } else {
  1456. # that is a folder we found, but we don't handle that
  1457. }
  1458. }
  1459. # make all client create e new favorites list
  1460. SB_SERVER_Broadcast( $hash, "FAVORITES",
  1461. "FLUSH dont care", undef );
  1462. # find all the names and broadcast to our clients
  1463. $favsetstring = "favorites:";
  1464. foreach my $titi ( keys %{$favorites{$name}} ) {
  1465. Log3( $hash, 5, "SB_SERVER_ParseFavorites($name): " .
  1466. "ID:" . $favorites{$name}{$titi}{ID} .
  1467. " Name:" . $favorites{$name}{$titi}{Name} . " $titi" );
  1468. $favsetstring .= "$titi,";
  1469. SB_SERVER_Broadcast( $hash, "FAVORITES",
  1470. "ADD $name $favorites{$name}{$titi}{ID} " .
  1471. "$titi $favorites{$name}{$titi}{URL} $favorites{$name}{$titi}{Name}", undef ); # CD 0009 URL an Player schicken
  1472. }
  1473. #chop( $favsetstring );
  1474. #$favsetstring .= " ";
  1475. }
  1476. # ----------------------------------------------------------------------------
  1477. # generate a UID for the hash entry from the name
  1478. # ----------------------------------------------------------------------------
  1479. sub SB_SERVER_FavoritesName2UID( $ ) {
  1480. my $namestr = shift( @_ );
  1481. # eliminate spaces
  1482. $namestr = join( "_", split( " ", $namestr ) ); # CD 0009 Leerzeichen durch _ ersetzen statt löschen
  1483. # CD 0009 verschiedene Sonderzeichen ersetzen und nicht mehr löschen
  1484. my %Sonderzeichen = ("ä" => "ae", "Ä" => "Ae", "ü" => "ue", "Ü" => "Ue", "ö" => "oe", "Ö" => "Oe", "ß" => "ss",
  1485. "é" => "e", "è" => "e", "ë" => "e", "à" => "a", "ç" => "c" );
  1486. my $Sonderzeichenkeys = join ("|", keys(%Sonderzeichen));
  1487. $namestr =~ s/($Sonderzeichenkeys)/$Sonderzeichen{$1}/g;
  1488. # CD 0009
  1489. # this defines the regexp. Please add new stuff with the seperator |
  1490. # CD 0003 changed öÜ to ö|Ü
  1491. my $tobereplaced = '[Ä|ä|Ö|ö|Ü|ü|\[|\]|\{|\}|\(|\)|\\\\|,|:|\?|' . # CD 0011 ,:? hinzugefügt
  1492. '\/|\'|\.|\"|\^|°|\$|\||%|@|&|\+]'; # CD 0009 + hinzugefügt
  1493. $namestr =~ s/$tobereplaced//g;
  1494. return( $namestr );
  1495. }
  1496. # ----------------------------------------------------------------------------
  1497. # push a command to the buffer
  1498. # ----------------------------------------------------------------------------
  1499. sub SB_SERVER_CMDStackPush( $$ ) {
  1500. my ( $hash, $cmd ) = @_;
  1501. my $name = $hash->{NAME};
  1502. my $n = $SB_SERVER_CmdStack{$name}{last_n};
  1503. $n=0 if(!defined($n)); # CD 0007
  1504. if( $n > AttrVal( $name, "maxcmdstack", 200 ) ) {
  1505. Log3( $hash, 5, "SB_SERVER_CMDStackPush($name): limit reached" );
  1506. SB_SERVER_CMDStackPop($hash); # CD 0007 added
  1507. #return; # CD 0007 disabled
  1508. }
  1509. $SB_SERVER_CmdStack{$name}{$n}{CMD} = $cmd;
  1510. $SB_SERVER_CmdStack{$name}{$n}{TS} = time(); # CD 0007
  1511. $n = $n + 1;
  1512. $SB_SERVER_CmdStack{$name}{last_n} = $n;
  1513. $SB_SERVER_CmdStack{$name}{first_n} = $n if (!defined($SB_SERVER_CmdStack{$name}{first_n})); # CD 0007
  1514. # update overall number of entries
  1515. $SB_SERVER_CmdStack{$name}{cnt} = $SB_SERVER_CmdStack{$name}{last_n} -
  1516. $SB_SERVER_CmdStack{$name}{first_n} + 1;
  1517. $hash->{CMDSTACK}=$SB_SERVER_CmdStack{$name}{cnt}; # CD 0007
  1518. }
  1519. # ----------------------------------------------------------------------------
  1520. # pop a command from the buffer
  1521. # ----------------------------------------------------------------------------
  1522. sub SB_SERVER_CMDStackPop( $ ) {
  1523. my ( $hash ) = @_;
  1524. my $name = $hash->{NAME};
  1525. my $n = $SB_SERVER_CmdStack{$name}{first_n};
  1526. $n=0 if(!defined($n)); # CD 0007
  1527. my $res = "";
  1528. # return the first element of the list
  1529. if( defined( $SB_SERVER_CmdStack{$name}{$n} ) ) {
  1530. $res = $SB_SERVER_CmdStack{$name}{$n}{CMD};
  1531. $res = "empty" if($SB_SERVER_CmdStack{$name}{$n}{TS}<time()-300); # CD 0007 drop commands older than 5 minutes
  1532. } else {
  1533. $res = "empty";
  1534. }
  1535. # and now remove the first element
  1536. delete( $SB_SERVER_CmdStack{$name}{$n} );
  1537. $n = $n + 1;
  1538. if ( $n <= $SB_SERVER_CmdStack{$name}{last_n} ) { # CD 0000 changed first_n to last_n
  1539. $SB_SERVER_CmdStack{$name}{first_n} = $n;
  1540. # update overall number of entries
  1541. $SB_SERVER_CmdStack{$name}{cnt} = $SB_SERVER_CmdStack{$name}{last_n} -
  1542. $SB_SERVER_CmdStack{$name}{first_n} + 1;
  1543. } else {
  1544. # end of list reached
  1545. $SB_SERVER_CmdStack{$name}{last_n} = 0;
  1546. $SB_SERVER_CmdStack{$name}{first_n} = 0;
  1547. $SB_SERVER_CmdStack{$name}{cnt} = 0;
  1548. }
  1549. $hash->{CMDSTACK}=$SB_SERVER_CmdStack{$name}{cnt}; # CD 0007
  1550. return( $res );
  1551. }
  1552. # CD 0011 start
  1553. # ----------------------------------------------------------------------------
  1554. # parse the list of known alarm playlists
  1555. # ----------------------------------------------------------------------------
  1556. sub SB_SERVER_ParseServerAlarmPlaylists( $$ ) {
  1557. my( $hash, $dataptr ) = @_;
  1558. my $name = $hash->{NAME};
  1559. Log3( $hash, 4, "SB_SERVER_ParseServerAlarmPlaylists($name): called" );
  1560. # force all clients to delete alarm playlists
  1561. SB_SERVER_Broadcast( $hash, "ALARMPLAYLISTS",
  1562. "FLUSH dont care", undef );
  1563. my @r=split("category:",join(" ",@{$dataptr}));
  1564. foreach my $a (@r){
  1565. my $i1=index($a," title:");
  1566. my $i2=index($a," url:");
  1567. my $i3=index($a," singleton:");
  1568. if (($i1!=-1)&&($i2!=-1)&&($i3!=-1)) {
  1569. my $url=substr($a,$i2+5,$i3-$i2-5);
  1570. $url=substr($a,$i1+7,$i2-$i1-7) if ($url eq "");
  1571. my $pn=SB_SERVER_FavoritesName2UID(decode('utf-8',$url));
  1572. SB_SERVER_Broadcast( $hash, "ALARMPLAYLISTS",
  1573. "ADD $pn category ".substr($a,0,$i1), undef );
  1574. SB_SERVER_Broadcast( $hash, "ALARMPLAYLISTS",
  1575. "ADD $pn title ".substr($a,$i1+7,$i2-$i1-7), undef );
  1576. SB_SERVER_Broadcast( $hash, "ALARMPLAYLISTS",
  1577. "ADD $pn url $url", undef );
  1578. }
  1579. }
  1580. }
  1581. # CD 0011 end
  1582. # ----------------------------------------------------------------------------
  1583. # parse the list of known Playlists
  1584. # ----------------------------------------------------------------------------
  1585. sub SB_SERVER_ParseServerPlaylists( $$ ) {
  1586. my( $hash, $dataptr ) = @_;
  1587. my $name = $hash->{NAME};
  1588. Log3( $hash, 4, "SB_SERVER_ParseServerPlaylists($name): called" );
  1589. my $namebuf = "";
  1590. my $uniquename = "";
  1591. my $idbuf = -1;
  1592. # typically the start index being a number
  1593. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  1594. shift( @{$dataptr} );
  1595. } else {
  1596. Log3( $hash, 5, "SB_SERVER_ParseServerPlaylists($name): entry is " .
  1597. "not the start number" );
  1598. return;
  1599. }
  1600. # typically the max index being a number
  1601. if( $dataptr->[ 0 ] =~ /^([0-9])*/ ) {
  1602. shift( @{$dataptr} );
  1603. } else {
  1604. Log3( $hash, 5, "SB_SERVER_ParseServerPlaylists($name): entry is " .
  1605. "not the end number" );
  1606. return;
  1607. }
  1608. my $datastr = join( " ", @{$dataptr} );
  1609. Log3( $hash, 5, "SB_SERVER_ParseServerPlaylists($name): data to parse: " .
  1610. $datastr );
  1611. # make all client create a new favorites list
  1612. SB_SERVER_Broadcast( $hash, "PLAYLISTS",
  1613. "FLUSH dont care", undef );
  1614. my @data1 = split( " ", $datastr );
  1615. foreach( @data1 ) {
  1616. if( $_ =~ /^(id:)(.*)/ ) {
  1617. Log3( $hash, 5, "SB_SERVER_ParseServerPlaylists($name): " .
  1618. "id:$idbuf name:$namebuf " );
  1619. if( $idbuf != -1 ) {
  1620. $uniquename = SB_SERVER_FavoritesName2UID( $namebuf ); # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
  1621. SB_SERVER_Broadcast( $hash, "PLAYLISTS",
  1622. "ADD $namebuf $idbuf $uniquename", undef );
  1623. }
  1624. $idbuf = $2;
  1625. $namebuf = "";
  1626. $uniquename = "";
  1627. next;
  1628. } elsif( $_ =~ /^(playlist:)(.*)/ ) {
  1629. $namebuf = $2;
  1630. next;
  1631. } elsif( $_ =~ /^(count:)([0-9]*)/ ) {
  1632. # the last entry of the return
  1633. Log3( $hash, 5, "SB_SERVER_ParseServerPlaylists($name): " .
  1634. "id:$idbuf name:$namebuf " );
  1635. if( $idbuf != -1 ) {
  1636. $uniquename = SB_SERVER_FavoritesName2UID( $namebuf ); # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
  1637. SB_SERVER_Broadcast( $hash, "PLAYLISTS",
  1638. "ADD $namebuf $idbuf $uniquename", undef );
  1639. }
  1640. } else {
  1641. $namebuf .= "_" . $_;
  1642. next;
  1643. }
  1644. }
  1645. return;
  1646. }
  1647. # CD 0008 start
  1648. sub SB_SERVER_CheckConnection($) {
  1649. my($in ) = shift;
  1650. my(undef,$name) = split(':',$in);
  1651. my $hash = $defs{$name};
  1652. Log3( $hash, 3, "SB_SERVER_CheckConnection($name): STATE: " . $hash->{STATE} . " power: ". ReadingsVal( $name, "power", "X" )); # CD 0009 level 2->3
  1653. if(ReadingsVal( $name, "power", "X" ) ne "on") {
  1654. Log3( $hash, 3, "SB_SERVER_CheckConnection($name): forcing power on"); # CD 0009 level 2->3
  1655. $hash->{helper}{pingCounter}=0;
  1656. SB_SERVER_Broadcast( $hash, "SERVER",
  1657. "IP " . $hash->{IP} . ":" .
  1658. AttrVal( $name, "httpport", "9000" ) );
  1659. $hash->{helper}{doBroadcast}=1;
  1660. SB_SERVER_LMS_Status( $hash );
  1661. if( AttrVal( $name, "doalivecheck", "false" ) eq "false" ) {
  1662. readingsSingleUpdate( $hash, "power", "on", 1 );
  1663. } elsif( AttrVal( $name, "doalivecheck", "false" ) eq "true" ) {
  1664. # start the alive checking mechanism
  1665. # CD 0020 SB_SERVER_tcb_Alive verwenden
  1666. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  1667. InternalTimer( gettimeofday() +
  1668. AttrVal( $name, "alivetimer", 10 ),
  1669. "SB_SERVER_tcb_Alive",
  1670. "SB_SERVER_Alive:$name",
  1671. 0 );
  1672. }
  1673. }
  1674. RemoveInternalTimer( "CheckConnection:$name");
  1675. }
  1676. # CD 0008 end
  1677. # ----------------------------------------------------------------------------
  1678. # the Notify function
  1679. # ----------------------------------------------------------------------------
  1680. sub SB_SERVER_Notify( $$ ) {
  1681. my ( $hash, $dev_hash ) = @_;
  1682. my $name = $hash->{NAME}; # own name / hash
  1683. my $devName = $dev_hash->{NAME}; # Device that created the events
  1684. # CD start
  1685. if ($dev_hash->{NAME} eq "global" && grep (m/^INITIALIZED$|^REREADCFG$/,@{$dev_hash->{CHANGED}})){
  1686. DevIo_OpenDev($hash, 0, "SB_SERVER_DoInit" );
  1687. }
  1688. # CD end
  1689. #Log3( $hash, 4, "SB_SERVER_Notify($name): called" .
  1690. # "Own:" . $name . " Device:" . $devName );
  1691. # CD 0008 start
  1692. if($devName eq $name ) {
  1693. if (grep (m/^DISCONNECTED$/,@{$dev_hash->{CHANGED}})) {
  1694. Log3( $hash, 3, "SB_SERVER_Notify($name): DISCONNECTED - STATE: " . $hash->{STATE} . " power: ". ReadingsVal( $name, "power", "X" )); # CD 0009 level 2->3
  1695. RemoveInternalTimer( "CheckConnection:$name");
  1696. }
  1697. if (grep (m/^CONNECTED$/,@{$dev_hash->{CHANGED}})) {
  1698. Log3( $hash, 3, "SB_SERVER_Notify($name): CONNECTED - STATE: " . $hash->{STATE} . " power: ". ReadingsVal( $name, "power", "X" )); # CD 0009 level 2->3
  1699. InternalTimer( gettimeofday() + 2,
  1700. "SB_SERVER_CheckConnection",
  1701. "CheckConnection:$name",
  1702. 0 );
  1703. }
  1704. }
  1705. # CD 0008 end
  1706. if( $devName eq $hash->{RCCNAME} ) {
  1707. if( ReadingsVal( $hash->{RCCNAME}, "state", "off" ) eq "off" ) {
  1708. RemoveInternalTimer( $hash );
  1709. # CD 0020 SB_SERVER_tcb_Alive verwenden
  1710. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  1711. InternalTimer( gettimeofday() + 10,
  1712. "SB_SERVER_tcb_Alive",
  1713. "SB_SERVER_Alive:$name",
  1714. 0 );
  1715. # CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
  1716. #DevIo_CloseDev( $hash );
  1717. DevIo_Disconnected( $hash );
  1718. $hash->{helper}{pingCounter}=9999; # CD 0007
  1719. $hash->{CLICONNECTION} = "off"; # CD 0007
  1720. # CD 0005 set state after DevIo_CloseDev
  1721. # CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
  1722. SB_SERVER_setStates($hash, "disconnected");
  1723. } elsif( ReadingsVal( $hash->{RCCNAME}, "state", "off" ) eq "on" ) {
  1724. RemoveInternalTimer( $hash );
  1725. # do an update of the status, but SB CLI must come up
  1726. # CD 0020 SB_SERVER_tcb_Alive verwenden
  1727. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  1728. InternalTimer( gettimeofday() + 20,
  1729. "SB_SERVER_tcb_Alive",
  1730. "SB_SERVER_Alive:$name",
  1731. 0 );
  1732. } else {
  1733. return( undef );
  1734. }
  1735. return( "" );
  1736. # CD 0007 start
  1737. } elsif( $devName eq $hash->{PRESENCENAME} ) {
  1738. if(grep (m/^present$|^absent$/,@{$dev_hash->{CHANGED}})) {
  1739. Log3( $hash, 3, "SB_SERVER_Notify($name): $devName changed to ". join(" ",@{$dev_hash->{CHANGED}})); # CD 0023 loglevel 2->3
  1740. # CD 0023 start
  1741. if (defined($hash->{helper}{lastPRESENCEstate})) {
  1742. if($hash->{helper}{lastPRESENCEstate} eq $dev_hash->{CHANGED}[0]) {
  1743. # nichts geändert
  1744. return( undef );
  1745. }
  1746. }
  1747. $hash->{helper}{lastPRESENCEstate}=$dev_hash->{CHANGED}[0];
  1748. # CD 0023 end
  1749. RemoveInternalTimer( $hash );
  1750. # do an update of the status, but SB CLI must come up
  1751. # CD 0020 SB_SERVER_tcb_Alive verwenden
  1752. RemoveInternalTimer( "SB_SERVER_Alive:$name");
  1753. InternalTimer( gettimeofday() + 10,
  1754. "SB_SERVER_tcb_Alive",
  1755. "SB_SERVER_Alive:$name",
  1756. 0 );
  1757. return( "" );
  1758. } else {
  1759. return( undef );
  1760. }
  1761. # CD 0007 end
  1762. } else {
  1763. return( undef );
  1764. }
  1765. }
  1766. # ----------------------------------------------------------------------------
  1767. # start up the LMS server status
  1768. # ----------------------------------------------------------------------------
  1769. sub SB_SERVER_LMS_Status( $ ) {
  1770. my ( $hash ) = @_;
  1771. my $name = $hash->{NAME}; # own name / hash
  1772. # CD 0007 login muss als erstes gesendet werden
  1773. $hash->{helper}{SB_SERVER_LMS_Status}=time();
  1774. if( ( $hash->{USERNAME} ne "?" ) &&
  1775. ( $hash->{PASSWORD} ne "?" ) ) {
  1776. DevIo_SimpleWrite( $hash, "login " .
  1777. $hash->{USERNAME} . " " .
  1778. $hash->{PASSWORD} . "\n",
  1779. 0 );
  1780. }
  1781. # subscribe us
  1782. DevIo_SimpleWrite( $hash, "listen 1\n", 0 );
  1783. # and get some info on the server
  1784. DevIo_SimpleWrite( $hash, "pref authorize ?\n", 0 );
  1785. DevIo_SimpleWrite( $hash, "version ?\n", 0 );
  1786. DevIo_SimpleWrite( $hash, "serverstatus 0 200\n", 0 );
  1787. DevIo_SimpleWrite( $hash, "favorites items 0 " .
  1788. AttrVal( $name, "maxfavorites", 100 ) . " want_url:1\n", 0 ); # CD 0009 url mit abfragen
  1789. DevIo_SimpleWrite( $hash, "playlists 0 200\n", 0 );
  1790. DevIo_SimpleWrite( $hash, "alarm playlists 0 300\n", 0 ); # CD 0011
  1791. return( true );
  1792. }
  1793. # CD 0006 start - added
  1794. # ----------------------------------------------------------------------------
  1795. # copied from DevIo.pm 7099
  1796. # ----------------------------------------------------------------------------
  1797. sub SB_SERVER_setStates($$)
  1798. {
  1799. my ($hash, $val) = @_;
  1800. $hash->{STATE} = $val;
  1801. setReadingsVal($hash, "state", $val, TimeNow());
  1802. }
  1803. # CD 0006 end
  1804. # ############################################################################
  1805. # No PERL code beyond this line
  1806. # ############################################################################
  1807. 1;
  1808. =pod
  1809. =item device
  1810. =item summary connect to a Logitech Media Server (LMS)
  1811. =item summary_DE Anbindung an Logitech Media Server (LMS)
  1812. =begin html
  1813. <a name="SB_SERVER"></a>
  1814. <h3>SB_SERVER</h3>
  1815. <ul>
  1816. <a name="SBserverdefine"></a>
  1817. <b>Define</b>
  1818. <ul>
  1819. <code>define &lt;name&gt; SB_SERVER &lt;ip[:cliserverport]&gt; [RCC:&lt;RCC&gt;] [WOL:&lt;WOL&gt;] [PRESENCE:&lt;PRESENCE&gt;] [USER:&lt;username&gt;] [PASSWORD:&lt;password&gt;]</code>
  1820. <br><br>
  1821. This module allows you in combination with the module SB_PLAYER to control a
  1822. Logitech Media Server (LMS) and connected Squeezebox Media Players.<br><br>
  1823. Attention: The <code>[:cliserverport]</code> parameter is
  1824. optional. You just need to configure it if you changed it on the LMS.
  1825. The default TCP port is 9090.<br><br>
  1826. <b>Optional</b>
  1827. <ul>
  1828. <li><code>&lt;[RCC]&gt;</code>: You can define a FHEM RCC Device, if you want to wake it up when you set the SB_SERVER on. </li>
  1829. <li><code>&lt;[WOL]&gt;</code>: You can define a FHEM WOL Device, if you want to wake it up when you set the SB_SERVER on. </li>
  1830. <li><code>&lt;[PRESENCE]&gt;</code>: You can define a FHEM PRESENCE Device that is used to check if the server is reachable. </li>
  1831. <li><code>&lt;username&gt;</code> and <code>&lt;password&gt;</code>: If your LMS is password protected you can define the credentials here. </li>
  1832. </ul><br>
  1833. </ul>
  1834. <a name="SBserverset"></a>
  1835. <b>Set</b>
  1836. <ul>
  1837. <code>set &lt;name&gt; &lt;command&gt;</code>
  1838. <br><br>
  1839. This module supports the following SB_Server related commands:<br><br>
  1840. <ul>
  1841. <li><b>abort</b> - Stops the connection to the server</li>
  1842. <li><b>addToFHEMUpdate</b> - Includes the modules in the FHEM update, needs to be executed only once</li>
  1843. <li><b>cliraw &lt;cli-command&gt;</b> - Sends a &lt;cli-command&gt; to the LMS CLI</li>
  1844. <li><b>on</b> - Tries to switch on the Server by WOL or RCC</li>
  1845. <li><b>removeFromFHEMUpdate</b> - Removes the modules from the FHEM update</li>
  1846. <li><b>renew</b> - Renews the connection to the server</li>
  1847. <li><b>rescan</b> - Starts the scan of the music library of the server</li>
  1848. <li><b>statusRequest</b> - Update of readings from server and configured players</li>
  1849. </ul>
  1850. <br>
  1851. </ul>
  1852. <a name="SBserverattr"></a>
  1853. <b>Attributes</b>
  1854. <ul>
  1855. <li><code>alivetimer &lt;sec&gt;</code><br>
  1856. Default: 120. Every &lt;sec&gt; seconds it is checked, whether the computer with its LMS is still reachable
  1857. – either via an internal ping (that leads regulary to problems) or via PRESENCE (preferred, no problems)
  1858. - and running.</li>
  1859. <li><code>doalivecheck &lt;true|false&gt;</code><br>
  1860. Switches the LMS-monitoring on or off.</li>
  1861. <li><code>httpport &lt;port&gt;</code><br>
  1862. Normally the http-port is set to 9000. If this ist NOT the case, you have to enter here the new
  1863. port-number. You can check the port-number of the LMS within its setup under Setup – Network – Web Server Port Number.</li>
  1864. <li><a name="SBserver_attribut_ignoredIPs"><code>ignoredIPs &lt;IP-Address[,IP-Address]&gt;</code>
  1865. </a><br />With this attribute you can define IP-addresses of players which will to be ignored by the server, e.g. "192.168.0.11,192.168.0.37"</li>
  1866. <li><a name="SBserver_attribut_ignoredMACs"><code>ignoredMACs &lt;MAC-Address[,MAC-Address]&gt;</code>
  1867. </a><br />With this attribute you can define MAC-addresses of players which will to be ignored by the server, e.g. "00:11:22:33:44:55,ff:ee:dd:cc:bb:aa"</li>
  1868. <li><code>maxcmdstack &lt;quantity&gt;</code><br>
  1869. By default the stack ist set up to 200. If the connection to the LMS is lost, up to &lt;quantity&gt;
  1870. commands are buffered. After the link is reconnected, commands, that are not older than five minutes,
  1871. are send to the LMS.</li>
  1872. <li><code>maxfavorites &lt;number&gt;</code><br>
  1873. Adjust here the maximal number of the favourites.</li>
  1874. </ul>
  1875. </ul>
  1876. =end html
  1877. =begin html_DE
  1878. <a name="SB_SERVER"></a>
  1879. <h3>SB_SERVER</h3>
  1880. <ul>
  1881. <a name="SBserverdefine"></a>
  1882. <b>Define</b>
  1883. <ul>
  1884. <code>define &lt;name&gt; SB_SERVER &lt;ip[:cliserverport]&gt; [RCC:&lt;RCC&gt;] [WOL:&lt;WOL&gt;] [PRESENCE:&lt;PRESENCE&gt;] [USER:&lt;username&gt;] [PASSWORD:&lt;password&gt;]</code>
  1885. <br><br>
  1886. Diese Modul erm&ouml;glicht es - zusammen mit dem Modul SB_PLAYER - einen
  1887. Logitech Media Server (LMS) und die angeschlossenen Squeezebox Media
  1888. Player zu steuern.<br><br>
  1889. Achtung: Die Angabe des Parameters <code>[:cliserverport]</code> ist
  1890. optional und nur dann erforderlich, wenn die Portnummer im LMS vom
  1891. Standardwert (TCP Port 9090) abweichend eingetragen wurde.<br><br>
  1892. <b>Optionen</b>
  1893. <ul>
  1894. <li><code>&lt;[RCC]&gt;</code>: Hier kann ein FHEM RCC Device angegeben werden mit dem der Server aufgeweckt und eingeschaltet werden kann.</li>
  1895. <li><code>&lt;[WOL]&gt;</code>: Hier kann ein FHEM WOL Device angegeben werden mit dem der Server aufgeweckt und eingeschaltet werden kann.</li>
  1896. <li><code>&lt;[PRESENCE]&gt;</code>: Hier kann ein FHEM PRESENCE Device angegeben werden mit dem die Erreichbarkeit des Servers &uuml;berpr&uuml;ft werden kann.</li>
  1897. <li><code>&lt;username&gt;</code> and <code>&lt;password&gt;</code>: Falls der Server durch ein Passwort gesichert wurde, k&ouml;nnen hier die notwendigen Angaben für den Serverzugang angegeben werden.</li>
  1898. </ul><br>
  1899. </ul>
  1900. <a name="SBserverset"></a>
  1901. <b>Set</b>
  1902. <ul>
  1903. <code>set &lt;name&gt; &lt;command&gt;</code>
  1904. <br><br>
  1905. Dieses Modul unterst&uuml;tzt folgende SB_SERVER relevanten Befehle:<br><br>
  1906. <ul>
  1907. <li><b>abort</b> - Bricht die Verbindung zum Server ab.</li>
  1908. <li><b>addToFHEMUpdate</b> - F&uuml;gt die Module dem FHEM-Update hinzu, muss nur einmalig ausgef&uuml;hrt werden.</li>
  1909. <li><b>cliraw &lt;cli-command&gt;</b> - Sendet einen CLI-Befehl an das LMS CLI</li>
  1910. <li><b>on</b> - Versucht den Server per WOL oder RCC einzuschalten.</li>
  1911. <li><b>removeFromFHEMUpdate</b> - Schlie&szlig;t die Module vom FHEM-Update aus.</li>
  1912. <li><b>renew</b> - Erneuert die Verbindung zum Server.</li>
  1913. <li><b>rescan</b> - Startet einen Scan der Musikbibliothek f&uuml;r alle im Server angegebenen Verzeichnisse.</li>
  1914. <li><b>statusRequest</b> - Aktualisiert die Readings von Server und konfigurierten Playern.</li>
  1915. </ul>
  1916. <br>
  1917. </ul>
  1918. <a name="SBserverattr"></a>
  1919. <b>Attribute</b>
  1920. <ul>
  1921. <li><code>alivetimer &lt;sec&gt;</code><br>
  1922. Default 120. Alle &lt;sec&gt; Sekunden wird &uuml;berpr&uuml;ft, ob der Rechner mit dem LMS noch erreichbar ist
  1923. - entweder über internen Ping (f&uuml;hrt zu regelm&auml;&szlig;igen H&auml;ngern von FHEM) oder PRESENCE (bevorzugt,
  1924. keine H&auml;nger) - und ob der LMS noch l&auml;uft.</li>
  1925. <li><code>doalivecheck &lt;true|false&gt;</code><br>
  1926. &Uuml;berwachung des LMS ein- oder auschalten.</li>
  1927. <li><code>httpport &lt;port&gt;</code><br>
  1928. Im Normalfall ist der http-Port auf 9000 eingestellt. Sollte dies NICHT der Fall sein muss hier die ge&auml;nderte
  1929. Portnummer eingetragen werden. Zur &Uuml;berpr&uuml;fung kann im Server unter Einstellungen – Erweitert –Netzwerk
  1930. - Anschlussnummer des Webservers nachgeschlagen werden.</li>
  1931. <li><a name="SBserver_attribut_ignoredIPs"><b><code>ignoredIPs &lt;IP-Adresse&gt;[,IP-Adresse]</code></b>
  1932. </a><br />Mit diesem Attribut kann die automatische Erkennung dedizierter Ger&auml;te durch die Angabe derer IP-Adressen unterdrückt werden, z.B. "192.168.0.11,192.168.0.37"</li>
  1933. <li><a name="SBserver_attribut_ignoredMACs"><b><code>ignoredMACs &lt;MAC-Adresse&gt;[,MAC-Adresse]</code></b>
  1934. </a><br />Mit diesem Attribut kann die automatische Erkennung dedizierter Ger&auml;te durch die Angabe derer MAC-Adressen unterdrückt werden, z.B. "00:11:22:33:44:55,ff:ee:dd:cc:bb:aa"</li>
  1935. <li><code>maxcmdstack &lt;Anzahl&gt;</code><br>
  1936. Default ist der Stack auf eine Gr&ouml;&szlig;e von 200 eingestellt. Wenn die Verbindung zum LMS unterbrochen ist,
  1937. werden bis zu &lt;Anzahl&gt; Befehle zwischengespeichert. Nach dem Verbindungsaufbau werden die Befehle,
  1938. die nicht &auml;lter als 5 Minuten sind, an den LMS geschickt.</li>
  1939. <li><code>maxfavorites &lt;Anzahl&gt;</code><br>
  1940. Die maximale Anzahl der Favoriten wird hier eingestellt.</li>
  1941. </ul>
  1942. </ul>
  1943. =end html_DE
  1944. =cut