diff --git a/fhem/contrib/98_SB_PLAYER.pm b/fhem/contrib/98_SB_PLAYER.pm index 15bc1ef47..fbd7cb135 100644 --- a/fhem/contrib/98_SB_PLAYER.pm +++ b/fhem/contrib/98_SB_PLAYER.pm @@ -12,12 +12,12 @@ # Written by bugster_de # # Contributions from: Siggi85, Oliv06, ChrisD, Markus M., Matthew, KernSani, -# Heppel, Eberhard +# Heppel, Eberhard, Elektrolurch # # ############################################################################## # # This is absolutley open source. Please feel free to use just as you -# like. Please note, that no warranty is given and no liability +# like. Please note, that no warranty is given and no liability # granted # # ############################################################################## @@ -45,9 +45,6 @@ # DISPLAYTYPE what sort of display is there, if any # # ############################################################################## -# based on 98_SB_PLAYER.pm 9752 beta 0060 CD/MM/Matthew/Heppel -# ############################################################################## - package main; use strict; @@ -64,6 +61,8 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; use constant { true => 1, false => 0 }; +sub SB_PLAYER_SonginfoHandleQueue($); # CD 0075 + # the list of favorites # CD 0010 moved to $hash->{helper}{SB_PLAYER_Favs}, fixes problem on module reload #my %SB_PLAYER_Favs; @@ -112,9 +111,9 @@ my %ttsstates = ( 0 =>'idle', 1000=>'active', 2004=>'Text2Speech busy, waiting', 2006=>'Text2Speech active'); - + my $SB_PLAYER_hasDataDumper = 1; # CD 0036 - + # ---------------------------------------------------------------------------- # Initialisation routine called upon start-up of FHEM # ---------------------------------------------------------------------------- @@ -122,7 +121,7 @@ sub SB_PLAYER_Initialize( $ ) { my ($hash) = @_; # the commands we provide to FHEM - # installs the respecitive call-backs for FHEM. The call back in quotes + # installs the respecitive call-backs for FHEM. The call back in quotes # must be realised as a sub later on in the file $hash->{DefFn} = "SB_PLAYER_Define"; $hash->{UndefFn} = "SB_PLAYER_Undef"; @@ -139,22 +138,27 @@ sub SB_PLAYER_Initialize( $ ) { # CD 0032 $hash->{NotifyFn} = "SB_PLAYER_Notify"; - - # the attributes we have. Space separated list of attribute values in + + # the attributes we have. Space separated list of attribute values in # the form name:default1,default2 - $hash->{AttrList} = "IODev ignore:1,0 do_not_notify:1,0 "; - $hash->{AttrList} .= "volumeStep volumeLimit "; - $hash->{AttrList} .= "ttslanguage:de,en,fr ttslink "; + $hash->{AttrList} = "IODev do_not_notify:1,0 "; # CD 0077 ignore:1,0 entfernt, nicht verwendet + $hash->{AttrList} .= "volumeStep volumeLimit "; + $hash->{AttrList} .= "ttslanguage:de,en,fr ttslink:multiple,Google,VoiceRSS "; $hash->{AttrList} .= "donotnotify:true,false "; - $hash->{AttrList} .= "idismac:true,false "; - $hash->{AttrList} .= "serverautoon:true,false "; + $hash->{AttrList} .= "idismac "; # CD 0077 nicht verwendet, entfernen + $hash->{AttrList} .= "serverautoon "; # CD 0077 nicht verwendet, entfernen $hash->{AttrList} .= "fadeinsecs "; $hash->{AttrList} .= "amplifier:on,play "; $hash->{AttrList} .= "coverartheight:50,100,200 "; $hash->{AttrList} .= "coverartwidth:50,100,200 "; # CD 0028 $hash->{AttrList} .= "ttsVolume "; - $hash->{AttrList} .= "ttsOptions "; + $hash->{AttrList} .= "ttsOptions:multiple-strict,debug,debugsaverestore,unsync,nosaverestore,forcegroupon,ignorevolumelimit,eventondone "; # CD 0062 Auswahl vorgeben + + $hash->{AttrList} .= "trackPositionQueryInterval "; # CD 0064 + $hash->{AttrList} .= "sortFavorites:1,0 sortPlaylists:1,0 "; # CD 0064 + $hash->{AttrList} .= "volumeOffset "; # CD 0065 + # CD 0030 $hash->{AttrList} .= "ttsDelay "; # CD 0032 @@ -168,23 +172,24 @@ sub SB_PLAYER_Initialize( $ ) { $hash->{AttrList} .= "updateReadingsOnSet:true,false "; # CD 0017 $hash->{AttrList} .= "statusRequestInterval "; # CD 0037 $hash->{AttrList} .= "syncedNamesSource:LMS,FHEM "; # CD 0055 + $hash->{AttrList} .= "ftuiSupport:1,0 "; # CD 0065 $hash->{AttrList} .= $readingFnAttributes; - + # CD 0036 aus 37_sonosBookmarker eval "use Data::Dumper"; - $SB_PLAYER_hasDataDumper = 0 if($@); + $SB_PLAYER_hasDataDumper = 0 if($@); } # CD 0007 start # ---------------------------------------------------------------------------- -# Attr functions +# Attr functions # ---------------------------------------------------------------------------- sub SB_PLAYER_Attr( @ ) { my $cmd = shift( @_ ); my $name = shift( @_ ); my @args = @_; my $hash = $defs{$name}; - + Log( 4, "SB_PLAYER_Attr($name): called with @args" ); if( $args[ 0 ] eq "syncVolume" ) { @@ -199,15 +204,15 @@ sub SB_PLAYER_Attr( @ ) { IOWrite( $hash, "$hash->{PLAYERMAC} playerpref syncVolume ?\n" ); } } else { - + } } # CD 0012 start - bei Änderung des Attributes Zustand überprüfen elsif( $args[ 0 ] eq "amplifier" ) { RemoveInternalTimer( "DelayAmplifier:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_DelayAmplifier", # CD 0014 Name geändert - "DelayAmplifier:$name", + "DelayAmplifier:$name", 0 ); } # CD 0043 start @@ -225,7 +230,7 @@ sub SB_PLAYER_Attr( @ ) { return "missing value for amplifierDelayOff"; } } else { - + } } # CD 0043 end @@ -256,6 +261,7 @@ sub SB_PLAYER_Attr( @ ) { $hash->{helper}{ttsOptions}{internalsave}=1 if($opt=~ m/internalsave/); # CD 0029 $hash->{helper}{ttsOptions}{ignorevolumelimit}=1 if($opt=~ m/ignorevolumelimit/); # CD 0031 $hash->{helper}{ttsOptions}{doubleescape}=1 if($opt=~ m/doubleescape/); # CD 0059 + $hash->{helper}{ttsOptions}{eventondone}=1 if($opt=~ m/eventondone/); # CD 0061 } } else { return "invalid value for ttsOptions"; @@ -265,7 +271,7 @@ sub SB_PLAYER_Attr( @ ) { } } # CD 0030 - elsif( $args[ 0 ] eq "ttsDelay" ) { + elsif( $args[ 0 ] eq "ttsDelay" ) { if( $cmd eq "set" ) { if (defined($args[1])) { my @options=split(',',$args[1]); @@ -287,17 +293,17 @@ sub SB_PLAYER_Attr( @ ) { RemoveInternalTimer( $hash ); if( $cmd eq "set" ) { if (defined($args[1])) { - InternalTimer( gettimeofday() + $args[1], - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + $args[1], + "SB_PLAYER_GetStatus", + $hash, 0 ) if $args[1]>0; } else { return "missing value for statusRequestInterval"; } } else { - InternalTimer( gettimeofday() + 5, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + 5, + "SB_PLAYER_GetStatus", + $hash, 0 ); } } @@ -306,6 +312,68 @@ sub SB_PLAYER_Attr( @ ) { IOWrite( $hash, "$hash->{PLAYERMAC} status 0 500 tags:Kcu\n" ) if ($init_done>0); } # CD 0055 end + # CD 0064 start + elsif( $args[ 0 ] eq "trackPositionQueryInterval" ) { + if( $cmd eq "set" ) { + RemoveInternalTimer( "QueryElapsedTime:$name"); + if ((ReadingsVal($name,"playStatus","x") eq "playing")&&($args[1]>0)) { + InternalTimer( gettimeofday() + $args[1], + "SB_PLAYER_tcb_QueryElapsedTime", + "QueryElapsedTime:$name", + 0 ); + } + } + } + elsif( $args[ 0 ] eq "sortPlaylists" ) { + if( $cmd eq "set" ) { + if ($args[1] eq "1") { + my @radios = split(',',$hash->{SERVERPLAYLISTS}); + $hash->{SERVERPLAYLISTS} = join(',',sort { "\L$a" cmp "\L$b" } @radios); + } + } + } + elsif( $args[ 0 ] eq "sortFavorites" ) { + if( $cmd eq "set" ) { + if ($args[1] eq "1") { + my @radios = split(',',$hash->{FAVSTR}); + $hash->{FAVSTR} = join(',',sort { "\L$a" cmp "\L$b" } @radios); + } + } + } + # CD 0064 end + # CD 0065 start + elsif( $args[ 0 ] eq "ftuiSupport" ) { + my $dodelete=0; + + if( $cmd eq "set" ) { + if ($args[1] eq "0") { + $dodelete=1; + } elsif ($args[1] eq "1") { + delete($hash->{SONGINFOQUEUE}) if(defined($hash->{SONGINFOQUEUE})); # CD 0072 + if(defined($hash->{helper}{playlistIds})) { + $hash->{helper}{playlistInfoRetries}=5; # CD 0076 + my @ids=split(',',$hash->{helper}{playlistIds}); + foreach(@ids) { + IOWrite( $hash, $hash->{PLAYERMAC}." songinfo 0 100 track_id:".$_." tags:acdltuxNK\n" ) unless((defined($hash->{helper}{playlistInfo}{$_}) && !defined($hash->{helper}{playlistInfo}{$_}{remote})) || ($_==0)); + } + IOWrite( $hash, $hash->{PLAYERMAC}." FHEMupdatePlaylistInfoDone\n" ); + } + } + } else { + $dodelete=1; + } + # CD 0068 start + if($dodelete==1) { + delete($hash->{READINGS}{ftuiMedialist}) if defined($hash->{READINGS}{ftuiMedialist}); + delete($hash->{READINGS}{ftuiPlaylistsItems}) if defined($hash->{READINGS}{ftuiPlaylistsItems}); + delete($hash->{READINGS}{ftuiPlaylistsAlias}) if defined($hash->{READINGS}{ftuiPlaylistsAlias}); + delete($hash->{READINGS}{ftuiFavoritesItems}) if defined($hash->{READINGS}{ftuiFavoritesItems}); + delete($hash->{READINGS}{ftuiFavoritesAlias}) if defined($hash->{READINGS}{ftuiFavoritesAlias}); + } + # CD 0068 end + } + # CD 0065 end + return; # CD 0012 } @@ -317,18 +385,18 @@ sub SB_PLAYER_Attr( @ ) { # ---------------------------------------------------------------------------- sub SB_PLAYER_Define( $$ ) { my ( $hash, $def ) = @_; - + my $name = $hash->{NAME}; - + my @a = split("[ \t][ \t]*", $def); - + # do we have the right number of arguments? if( ( @a < 3 ) || ( @a > 5 ) ) { Log3( $hash, 1, "SB_PLAYER_Define: falsche Anzahl an Argumenten" ); return( "wrong syntax: define SB_PLAYER " . " " ); } - + # remove the name and our type # my $name = shift( @a ); shift( @a ); # name @@ -339,7 +407,7 @@ sub SB_PLAYER_Define( $$ ) { # the MAC adress is valid $hash->{PLAYERMAC} = lc($a[ 0 ]); # CD 0026 lc added } else { - my $msg = "SB_PLAYER_Define: playerid ist keine MAC Adresse " . + my $msg = "SB_PLAYER_Define: playerid ist keine MAC Adresse " . "im Format xx:xx:xx:xx:xx:xx oder xx-xx-xx-xx-xx-xx"; Log3( $hash, 1, $msg ); return( $msg ); @@ -423,7 +491,7 @@ sub SB_PLAYER_Define( $$ ) { # the IP and Port of the Server $hash->{SBSERVER} = "?"; } - + # preset the attributes # volume delta settings if( !defined( $attr{$name}{volumeStep} ) ) { @@ -445,10 +513,10 @@ sub SB_PLAYER_Define( $$ ) { $attr{$name}{donotnotify} = "true"; } - # is the ID the MAC adress - if( !defined( $attr{$name}{idismac} ) ) { - $attr{$name}{idismac} = "true"; - } + # is the ID the MAC adress # CD 0077 nicht verwendet, deaktiviert + #if( !defined( $attr{$name}{idismac} ) ) { + # $attr{$name}{idismac} = "true"; + #} # the language for text2speech if( !defined( $attr{$name}{ttslanguage} ) ) { @@ -457,14 +525,14 @@ sub SB_PLAYER_Define( $$ ) { # link to the text2speech engine if( !defined( $attr{$name}{ttslink} ) ) { - $attr{$name}{ttslink} = "http://translate.google.com" . + $attr{$name}{ttslink} = "http://translate.google.com" . "/translate_tts?ie=UTF-8&tl=&q=&client=tw-ob"; # CD 0045 Format geändert, &client=t&prev=input hinzugefügt, CD 0048 client=tw-ob verwenden } - - # turn on the server when player is used - if( !defined( $attr{$name}{serverautoon} ) ) { - $attr{$name}{serverautoon} = "true"; - } + + # turn on the server when player is used # CD 0077 nicht verwendet, deaktiviert + #if( !defined( $attr{$name}{serverautoon} ) ) { + # $attr{$name}{serverautoon} = "true"; + #} # amplifier on/off when play/pause or on/off if( !defined( $attr{$name}{amplifier} ) ) { @@ -485,119 +553,119 @@ sub SB_PLAYER_Define( $$ ) { # according to development guidelines of FHEM AV Module if( !defined( $hash->{READINGS}{presence}{VAL} ) ) { $hash->{READINGS}{presence}{VAL} = "?"; - $hash->{READINGS}{presence}{TIME} = $tn; + $hash->{READINGS}{presence}{TIME} = $tn; } # according to development guidelines of FHEM AV Module if( !defined( $hash->{READINGS}{power}{VAL} ) ) { $hash->{READINGS}{power}{VAL} = "?"; - $hash->{READINGS}{power}{TIME} = $tn; + $hash->{READINGS}{power}{TIME} = $tn; } # the last unkown command if( !defined( $hash->{READINGS}{lastunknowncmd}{VAL} ) ) { $hash->{READINGS}{lastunknowncmd}{VAL} = "none"; - $hash->{READINGS}{lastunknowncmd}{TIME} = $tn; + $hash->{READINGS}{lastunknowncmd}{TIME} = $tn; } # the last unkown IR command if( !defined( $hash->{READINGS}{lastir}{VAL} ) ) { $hash->{READINGS}{lastir}{VAL} = "?"; - $hash->{READINGS}{lastir}{TIME} = $tn; + $hash->{READINGS}{lastir}{TIME} = $tn; } # the id of the alarm we create # CD 0015 deaktiviert # if( !defined( $hash->{READINGS}{alarmid1}{VAL} ) ) { # $hash->{READINGS}{alarmid1}{VAL} = "none"; -# $hash->{READINGS}{alarmid1}{TIME} = $tn; +# $hash->{READINGS}{alarmid1}{TIME} = $tn; # } # if( !defined( $hash->{READINGS}{alarmid2}{VAL} ) ) { # $hash->{READINGS}{alarmid2}{VAL} = "none"; -# $hash->{READINGS}{alarmid2}{TIME} = $tn; +# $hash->{READINGS}{alarmid2}{TIME} = $tn; # } # values according to standard if( !defined( $hash->{READINGS}{playStatus}{VAL} ) ) { $hash->{READINGS}{playStatus}{VAL} = "?"; - $hash->{READINGS}{playStatus}{TIME} = $tn; + $hash->{READINGS}{playStatus}{TIME} = $tn; } if( !defined( $hash->{READINGS}{currentArtist}{VAL} ) ) { $hash->{READINGS}{currentArtist}{VAL} = "?"; - $hash->{READINGS}{currentArtist}{TIME} = $tn; + $hash->{READINGS}{currentArtist}{TIME} = $tn; } if( !defined( $hash->{READINGS}{currentAlbum}{VAL} ) ) { $hash->{READINGS}{currentAlbum}{VAL} = "?"; - $hash->{READINGS}{currentAlbum}{TIME} = $tn; + $hash->{READINGS}{currentAlbum}{TIME} = $tn; } if( !defined( $hash->{READINGS}{currentTitle}{VAL} ) ) { $hash->{READINGS}{currentTitle}{VAL} = "?"; - $hash->{READINGS}{currentTitle}{TIME} = $tn; + $hash->{READINGS}{currentTitle}{TIME} = $tn; } if( !defined( $hash->{READINGS}{favorites}{VAL} ) ) { $hash->{READINGS}{favorites}{VAL} = "not"; - $hash->{READINGS}{favorites}{TIME} = $tn; + $hash->{READINGS}{favorites}{TIME} = $tn; } if( !defined( $hash->{READINGS}{playlists}{VAL} ) ) { $hash->{READINGS}{playlists}{VAL} = "not"; - $hash->{READINGS}{playlists}{TIME} = $tn; + $hash->{READINGS}{playlists}{TIME} = $tn; } # for the FHEM AV Development Guidelinses # we use this to store the currently playing ID to later on return to if( !defined( $hash->{READINGS}{currentMedia}{VAL} ) ) { $hash->{READINGS}{currentMedia}{VAL} = "?"; - $hash->{READINGS}{currentMedia}{TIME} = $tn; + $hash->{READINGS}{currentMedia}{TIME} = $tn; } if( !defined( $hash->{READINGS}{currentPlaylistName}{VAL} ) ) { $hash->{READINGS}{currentPlaylistName}{VAL} = "?"; - $hash->{READINGS}{currentPlaylistName}{TIME} = $tn; + $hash->{READINGS}{currentPlaylistName}{TIME} = $tn; } if( !defined( $hash->{READINGS}{currentPlaylistUrl}{VAL} ) ) { $hash->{READINGS}{currentPlaylistUrl}{VAL} = "?"; - $hash->{READINGS}{currentPlaylistUrl}{TIME} = $tn; + $hash->{READINGS}{currentPlaylistUrl}{TIME} = $tn; } if( !defined( $hash->{READINGS}{volume}{VAL} ) ) { $hash->{READINGS}{volume}{VAL} = 0; - $hash->{READINGS}{volume}{TIME} = $tn; + $hash->{READINGS}{volume}{TIME} = $tn; } if( !defined( $hash->{READINGS}{volumeStraight}{VAL} ) ) { $hash->{READINGS}{volumeStraight}{VAL} = "?"; - $hash->{READINGS}{volumeStraight}{TIME} = $tn; + $hash->{READINGS}{volumeStraight}{TIME} = $tn; } if( !defined( $hash->{READINGS}{connected}{VAL} ) ) { - $hash->{READINGS}{connected}{VAL} = "?"; - $hash->{READINGS}{connected}{TIME} = $tn; + $hash->{READINGS}{connected}{VAL} = "?"; + $hash->{READINGS}{connected}{TIME} = $tn; } if( !defined( $hash->{READINGS}{signalstrength}{VAL} ) ) { $hash->{READINGS}{signalstrength}{VAL} = "?"; - $hash->{READINGS}{currentTitle}{TIME} = $tn; + $hash->{READINGS}{currentTitle}{TIME} = $tn; } if( !defined( $hash->{READINGS}{shuffle}{VAL} ) ) { $hash->{READINGS}{shuffle}{VAL} = "?"; - $hash->{READINGS}{currentTitle}{TIME} = $tn; + $hash->{READINGS}{currentTitle}{TIME} = $tn; } if( !defined( $hash->{READINGS}{repeat}{VAL} ) ) { $hash->{READINGS}{repeat}{VAL} = "?"; - $hash->{READINGS}{currentTitle}{TIME} = $tn; + $hash->{READINGS}{currentTitle}{TIME} = $tn; } if( !defined( $hash->{READINGS}{state}{VAL} ) ) { - $hash->{READINGS}{state}{VAL} = "?"; - $hash->{READINGS}{state}{TIME} = $tn; + $hash->{READINGS}{state}{VAL} = "?"; + $hash->{READINGS}{state}{TIME} = $tn; } # mrbreil 0047 start @@ -609,21 +677,24 @@ sub SB_PLAYER_Define( $$ ) { $hash->{helper}{ttsstate}=TTS_IDLE; # CD 0028 + $hash->{helper}{noStopEventUntil}=0; # CD 0063 + if (!defined($hash->{OLDDEF})) { # CD 0044 SB_PLAYER_LoadPlayerStates($hash) if($SB_PLAYER_hasDataDumper==1); # CD 0036 - + $hash->{helper}{playerStatusOK}=0; # CD 0042 $hash->{helper}{playerStatusOKCounter}=0; # CD 0042 $hash->{helper}{amplifierDelayOffStop}=0; # CD 0043 $hash->{helper}{amplifierDelayOffPower}=0; # CD 0043 $hash->{helper}{amplifierDelayOffPause}=0; # CD 0043 + $hash->{helper}{lmsvolume}=0; # CD 0065 } # do and update of the status - InternalTimer( gettimeofday() + 10, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + 10, + "SB_PLAYER_GetStatus", + $hash, 0 ); return( undef ); @@ -662,7 +733,9 @@ sub SB_PLAYER_tcb_DeleteRecallPause($) { sub SB_PLAYER_QueryElapsedTime($) { my ($hash) = @_; - if(!defined($hash->{helper}{lastTimeQuery})||($hash->{helper}{lastTimeQuery}{NAME}, "trackPositionQueryInterval", 5); # CD 0064 + + if(!defined($hash->{helper}{lastTimeQuery})||($hash->{helper}{lastTimeQuery}{helper}{lastTimeQuery}, now: ".gettimeofday(); $hash->{helper}{lastTimeQuery}=gettimeofday(); IOWrite( $hash, "$hash->{PLAYERMAC} time ?\n" ); @@ -676,17 +749,19 @@ sub SB_PLAYER_tcb_QueryElapsedTime( $ ) { my(undef,$name) = split(':',$in); my $hash = $defs{$name}; + my $qi=AttrVal($hash->{NAME}, "trackPositionQueryInterval", 5); # CD 0064 + SB_PLAYER_QueryElapsedTime($hash); RemoveInternalTimer( "QueryElapsedTime:$name"); - if (ReadingsVal($name,"playStatus","x") eq "playing") { - InternalTimer( gettimeofday() + 5, - "SB_PLAYER_tcb_QueryElapsedTime", - "QueryElapsedTime:$name", + if ((ReadingsVal($name,"playStatus","x") eq "playing")&&($qi>0)) { + InternalTimer( gettimeofday() + $qi, + "SB_PLAYER_tcb_QueryElapsedTime", + "QueryElapsedTime:$name", 0 ); } } # CD 0047 end - + # CD 0028 start sub SB_PLAYER_tcb_TTSRestore( $ ) { my($in ) = shift; @@ -699,11 +774,12 @@ sub SB_PLAYER_tcb_TTSRestore( $ ) { SB_PLAYER_LoadTalk($hash); } else { # CD 0033 end - if(!defined($hash->{helper}{ttsOptions}{nosaverestore})) { + if(!defined($hash->{helper}{ttsOptions}{nosaverestore}) && !defined($hash->{helper}{sgTalkActive})) { # CD 0063 sgTalkActive hinzugefügt SB_PLAYER_SetTTSState($hash,TTS_RESTORE,0,0); SB_PLAYER_Recall( $hash, "xxTTSxx del" ); } else { SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1); + delete $hash->{helper}{sgTalkActive} if defined($hash->{helper}{sgTalkActive}); # CD 0063 } } } @@ -719,13 +795,13 @@ sub SB_PLAYER_Parse( $$ ) { # xxxxxxxxxxxx cmd1 cmd2 cmd3 ... # where xxxxxxxxxxxx is derived from xx:xx:xx:xx:xx:xx # that needs to be done by the server - - Log3( $iohash, 5, "SB_PLAYER_Parse: called with $msg" ); - # storing the last in an array is necessery, for tagged responses + Log3( $iohash, 5, "SB_PLAYER_Parse: called with $msg" ); + + # storing the last in an array is necessary, for tagged responses my ( $modtype, $id, @data ) = split(":", $msg, 3 ); - - Log3( $iohash, 5, "SB_PLAYER_Parse: type:$modtype, ID:$id CMD:@data" ); + + Log3( $iohash, 5, "SB_PLAYER_Parse: type:$modtype, ID:$id CMD:@data" ); if( $modtype ne "SB_PLAYER" ) { # funny stuff happens at the disptach function @@ -733,14 +809,14 @@ sub SB_PLAYER_Parse( $$ ) { } # let's see what we got. Split the data at the space - # necessery, for tagged responses + # necessary, for tagged responses my @args = split( " ", join( " ", @data ) ); my $cmd = shift( @args ); my $hash = $modules{SB_PLAYER}{defptr}{$id}; if( !$hash ) { - Log3( undef, 3, "SB_PLAYER Unknown device with ID $id, " . + Log3( undef, 3, "SB_PLAYER Unknown device with ID $id, " . "please define it"); # do the autocreate; derive the unique id (MAC adress) @@ -751,23 +827,23 @@ sub SB_PLAYER_Parse( $$ ) { if( SB_PLAYER_IsValidMAC( $idbuf ) == 1 ) { # the MAC Adress is valid - Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is a valid " . + Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is a valid " . "MAC Adress" ); # this line supports autocreate return( "UNDEFINED SB_PLAYER_$id SB_PLAYER $idbuf" ); } else { # the MAC adress is not valid - Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is NOT " . + Log3( undef, 3, "SB_PLAYER_Parse: the unknown ID $id is NOT " . "a valid MAC Adress" ); return( undef ); } } - + # so the data is for us my $name = $hash->{NAME}; #return "" if(IsIgnored($name)); - Log3( $hash, 5, "SB_PLAYER_Parse: $name CMD:$cmd ARGS:@args..." ); + Log3( $hash, 5, "SB_PLAYER_Parse: $name CMD:$cmd ARGS:@args..." ); # what ever we have received, signal it $hash->{LASTANSWER} = "$cmd @args"; @@ -779,7 +855,7 @@ sub SB_PLAYER_Parse( $$ ) { if( $cmd eq "mixer" ) { if( $args[ 0 ] eq "volume" ) { - # update the volume + # update the volume if ($args[ 1 ] eq "?") { # it is a request } else { @@ -792,7 +868,7 @@ sub SB_PLAYER_Parse( $$ ) { SB_PLAYER_UpdateVolumeReadings( $hash, $args[ 1 ], true ); # CD 0007 start if((defined($hash->{helper}{setSyncVolume}) && ($hash->{helper}{setSyncVolume} != $args[ 1 ]))|| (!defined($hash->{helper}{setSyncVolume}))) { - SB_PLAYER_SetSyncedVolume($hash,$args[ 1 ]); + SB_PLAYER_SetSyncedVolume($hash,$args[ 1 ],0); } delete $hash->{helper}{setSyncVolume}; # CD 0007 end @@ -803,7 +879,7 @@ sub SB_PLAYER_Parse( $$ ) { } elsif( $cmd eq "remote" ) { if( defined( $args[ 0 ] ) ) { $hash->{ISREMOTESTREAM} = "$args[ 0 ]"; - } else { + } else { $hash->{ISREMOTESTREAM} = "0"; } @@ -824,12 +900,12 @@ sub SB_PLAYER_Parse( $$ ) { } else { readingsBulkUpdate( $hash, "playStatus", "paused" ); SB_PLAYER_Amplifier( $hash ); - } + } } elsif( $cmd eq "mode" ) { my $updateSyncedPlayers=0; # CD 0039 # a little more complex to fulfill FHEM Development guidelines - Log3( $hash, 5, "SB_PLAYER_Parse($name): mode:$cmd args:$args[0]" ); + Log3( $hash, 5, "SB_PLAYER_Parse($name): mode:$cmd args:$args[0]" ); if( $args[ 0 ] eq "play" ) { # CD 0014 start if(defined($hash->{helper}{recallPause})) { @@ -841,11 +917,14 @@ sub SB_PLAYER_Parse( $$ ) { SB_PLAYER_QueryElapsedTime( $hash ); # CD 0014 # CD 0047 start RemoveInternalTimer( "QueryElapsedTime:$name"); - InternalTimer( gettimeofday() + 5, - "SB_PLAYER_tcb_QueryElapsedTime", - "QueryElapsedTime:$name", - 0 ); - # CD 0047 end + my $qi=AttrVal($hash->{NAME}, "trackPositionQueryInterval", 5); # CD 0064 + if($qi>0) { # CD 0064 + InternalTimer( gettimeofday() + $qi, + "SB_PLAYER_tcb_QueryElapsedTime", + "QueryElapsedTime:$name", + 0 ); + # CD 0047 end + } } # CD 0014 # CD 0029 start if(defined($hash->{helper}{ttsOptions}{logplay})) { @@ -894,7 +973,7 @@ sub SB_PLAYER_Parse( $$ ) { } else { readingsBulkUpdate( $hash, "playStatus", $args[ 0 ] ); } - + # CD 0039 gesyncte Player aktualisieren if($updateSyncedPlayers==1) { if ($hash->{PLAYERMAC} eq $hash->{SYNCMASTER}) { @@ -922,7 +1001,7 @@ sub SB_PLAYER_Parse( $$ ) { } elsif( $cmd eq "playlist" ) { my $queryMode=1; # CD 0014 - + if( $args[ 0 ] eq "newsong" ) { # the song has changed, but we are easy and just ask the player IOWrite( $hash, "$hash->{PLAYERMAC} artist ?\n" ); @@ -938,12 +1017,12 @@ sub SB_PLAYER_Parse( $$ ) { $hash->{helper}{CoverOk}=0; # CD 0026 added # CD 0027 changed # CD 0025 bei lokalen Playlisten schneller abfragen if( $hash->{ISREMOTESTREAM} eq "0" ) { - InternalTimer( gettimeofday() + 3, + InternalTimer( gettimeofday() + 3, "SB_PLAYER_tcb_QueryCoverArt", "QueryCoverArt:$name", 0 ); } else { - InternalTimer( gettimeofday() + 10, + InternalTimer( gettimeofday() + 10, "SB_PLAYER_tcb_QueryCoverArt", # CD 0014 Name geändert "QueryCoverArt:$name", # CD 0014 Name geändert 0 ); @@ -974,15 +1053,15 @@ sub SB_PLAYER_Parse( $$ ) { if(defined($hash->{helper}{recallPause})) { IOWrite( $hash, "$hash->{PLAYERMAC} pause 1\n" ); RemoveInternalTimer( "recallPause:$name"); - InternalTimer( gettimeofday() + 0.5, - "SB_PLAYER_tcb_DeleteRecallPause", - "recallPause:$name", + InternalTimer( gettimeofday() + 0.5, + "SB_PLAYER_tcb_DeleteRecallPause", + "recallPause:$name", 0 ); } # CD 0014 end # the id is in the last return. ID not reported for radio stations - # so this will go wrong for e.g. Bayern 3 + # so this will go wrong for e.g. Bayern 3 # if( $args[ $#args ] =~ /(^[0-9]{1,3})/g ) { # readingsBulkUpdate( $hash, "currentMedia", $1 ); # } @@ -1053,7 +1132,7 @@ sub SB_PLAYER_Parse( $$ ) { # CD 0014 end if(defined($args[ 1 ]) && ($args[ 1 ] ne '?')) { # CD 0009 check empty name - 0011 ignore '?' shift( @args ); - readingsBulkUpdate( $hash, "currentPlaylistName", + readingsBulkUpdate( $hash, "currentPlaylistName", join( " ", @args ) ); my $pn=SB_SERVER_FavoritesName2UID(join( " ", @args )); # CD 0021 verschoben, decode hinzugefügt # CD 0023 decode entfernt # CD 0008 update playlists reading @@ -1094,6 +1173,7 @@ sub SB_PLAYER_Parse( $$ ) { } } } + $hash->{helper}{noStopEventUntil}=time()+2.0; # CD 0063 # CD 0021 end } elsif( $args[ 0 ] eq "clear" ) { readingsBulkUpdate( $hash, "currentPlaylistName", "none" ); @@ -1102,12 +1182,19 @@ sub SB_PLAYER_Parse( $$ ) { SB_PLAYER_GetStatus( $hash ); # CD 0014 } elsif( $args[ 0 ] eq "url" ) { shift( @args ); - readingsBulkUpdate( $hash, "currentPlaylistUrl", + readingsBulkUpdate( $hash, "currentPlaylistUrl", join( " ", @args ) ); } elsif( $args[ 0 ] eq "stop" ) { readingsBulkUpdate( $hash, "playStatus", "stopped" ); # CD 0012 'power off' durch 'playStatus stopped' ersetzt SB_PLAYER_Amplifier( $hash ); + # CD 0063 start + RemoveInternalTimer( "TriggerPlaylistStop:$name"); + InternalTimer( gettimeofday() + 1.0, + "SB_PLAYER_tcb_TriggerPlaylistStop", + "TriggerPlaylistStop:$name", + 0 ); + # CD 0063 stop # CD 0014 start } elsif( $args[ 0 ] eq "index" ) { readingsBulkUpdate( $hash, "playlistCurrentTrack", $args[ 1 ] eq '?' ? 0 : $args[ 1 ]+1 ); # Heppel 0056 @@ -1115,23 +1202,28 @@ sub SB_PLAYER_Parse( $$ ) { } elsif( $args[ 0 ] eq "addtracks" ) { $queryMode=0; SB_PLAYER_GetStatus( $hash ); + # CD 0068 start + } elsif( $args[ 0 ] eq "add" ) { + $queryMode=0; + SB_PLAYER_GetStatus( $hash ); + # CD 0068 end } elsif( $args[ 0 ] eq "delete" ) { $queryMode=0; #IOWrite( $hash, "$hash->{PLAYERMAC} alarm playlists 0 200\n" ); # CD 0016 get available elements for alarms # CD 0026 deaktiviert SB_PLAYER_GetStatus( $hash ); } elsif( $args[ 0 ] eq "load_done" ) { if($hash->{helper}{ttsstate}==TTS_PLAYING) { - #IOWrite( $hash, "$hash->{PLAYERMAC} playlist index +0\n"); + #IOWrite( $hash, "$hash->{PLAYERMAC} playlist index +0\n"); #IOWrite( $hash, "$hash->{PLAYERMAC} play\n" ); } if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST) { # CD 0030 start if(SB_PLAYER_GetTTSDelay($hash)>0) { RemoveInternalTimer( "TTSDelay:$name"); - InternalTimer( gettimeofday() + SB_PLAYER_GetTTSDelay($hash), + InternalTimer( gettimeofday() + SB_PLAYER_GetTTSDelay($hash), "SB_PLAYER_tcb_TTSDelay", - "TTSDelay:$name", - 0 ); + "TTSDelay:$name", + 0 ); SB_PLAYER_SetTTSState($hash,TTS_DELAY,1,0); } else { # CD 0030 end @@ -1139,9 +1231,9 @@ sub SB_PLAYER_Parse( $$ ) { IOWrite( $hash, "$hash->{PLAYERMAC} play\n" ); # CD 0038 Timeout hinzugefügt RemoveInternalTimer( "TimeoutTTSWaitForPlay:$name"); - InternalTimer( gettimeofday() + 10.00, + InternalTimer( gettimeofday() + 10.00, "SB_PLAYER_tcb_TimeoutTTSWaitForPlay", - "TimeoutTTSWaitForPlay:$name", + "TimeoutTTSWaitForPlay:$name", 0 ); # CD 0038 end } @@ -1212,19 +1304,33 @@ sub SB_PLAYER_Parse( $$ ) { if( defined( $args[ 2 ] ) ) { $hash->{PLAYERIP} .= ":$args[ 2 ]"; } - + } else { } + # CD 0063 start + } elsif( $cmd eq "playerdata" ) { + $hash->{MODEL} = $args[ 0 ] unless $args[ 0 ] eq 'unknown'; + $hash->{CANPOWEROFF} = $args[ 1 ] unless $args[ 1 ] eq 'unknown'; + $hash->{DISPLAYTYPE} = $args[ 2 ] unless $args[ 2 ] eq 'unknown'; + if( $args[ 3 ] ne 'unknown' ) { + readingsBulkUpdate( $hash, "connected", $args[ 3 ] ); + readingsBulkUpdate( $hash, "presence", "present" ); + } + $hash->{PLAYERIP} = "$args[ 4 ]" unless $args[ 4 ] eq 'unknown'; + if( defined( $args[ 5 ] ) ) { + $hash->{PLAYERIP} .= ":$args[ 5 ]"; + } + # CD 0063 end } elsif( $cmd eq "power" ) { if( !( @args ) ) { # no arguments were send with the Power command - # potentially this is a power toggle : should only happen + # potentially this is a power toggle : should only happen # when called with SB CLI } elsif( $args[ 0 ] eq "1" ) { readingsBulkUpdate( $hash, "state", "on" ); readingsBulkUpdate( $hash, "power", "on" ); - + SB_PLAYER_Amplifier( $hash ); } elsif( $args[ 0 ] eq "0" ) { #readingsBulkUpdate( $hash, "presence", "absent" ); # CD 0013 deaktiviert, power sagt nichts über presence @@ -1244,7 +1350,7 @@ sub SB_PLAYER_Parse( $$ ) { } else { readingsBulkUpdate( $hash, "signalstrength", "$args[ 0 ]" ); } - + } elsif( $cmd eq "alarm" ) { if( $args[ 0 ] eq "sound" ) { # fired when an alarm goes off @@ -1259,7 +1365,7 @@ sub SB_PLAYER_Parse( $$ ) { # fired when an alarm comes back from snooze DoTrigger($name,"alarmSnoozeEnd ".SB_PLAYER_FindAlarmId($hash, $args[ 1 ])) if (SB_PLAYER_check_eocr($hash,'alarmSnoozeEnd')); # CD 0046 # CD 0054 SB_PLAYER_check_eocr hinzugefügt } elsif( $args[ 0 ] eq "add" ) { - # fired when an alarm has been added. + # fired when an alarm has been added. # this setup goes wrong, when an alarm is defined manually # the last entry in the array shall contain th id my $idstr = $args[ $#args ]; @@ -1323,16 +1429,21 @@ sub SB_PLAYER_Parse( $$ ) { readingsBulkUpdate( $hash, "presence", "absent" ); readingsBulkUpdate( $hash, "state", "off" ); readingsBulkUpdate( $hash, "power", "off" ); + # CD 0074 bei disconnect playStatus und Timer zurücksetzen + readingsBulkUpdate( $hash, "playStatus", "stopped" ); + RemoveInternalTimer( $hash ); + RemoveInternalTimer( "QueryElapsedTime:$name"); + # CD 0074 end SB_PLAYER_Amplifier( $hash ); # CD 0031 wenn Player während TTS verschwindet Zustand zurücksetzen if(($hash->{helper}{ttsstate}>TTS_IDLE)&&($hash->{helper}{ttsstate}{helper}{savedPlayerState}{power}="off" if(defined($hash->{helper}{savedPlayerState})); SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0); RemoveInternalTimer( "TTSRestore:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSRestore", - "TTSRestore:$name", - 0 ); + "TTSRestore:$name", + 0 ); } # CD 0031 end } elsif( $args[ 0 ] eq "reconnect" ) { @@ -1357,9 +1468,9 @@ sub SB_PLAYER_Parse( $$ ) { if($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON) { # CD 0042 readingsBulkUpdate abwarten RemoveInternalTimer( "TTSStartAfterPowerOn:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSStartAfterPowerOn", - "TTSStartAfterPowerOn:$name", + "TTSStartAfterPowerOn:$name", 0 ); } # CD 0038 end @@ -1381,17 +1492,17 @@ sub SB_PLAYER_Parse( $$ ) { $hash->{helper}{savedPlayerState}{power}="off" if(defined($hash->{helper}{savedPlayerState})); SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0); RemoveInternalTimer( "TTSRestore:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSRestore", - "TTSRestore:$name", - 0 ); + "TTSRestore:$name", + 0 ); } # CD 0031 end } # CD 0000 end # CD 0010 start prefset server mute } elsif( $args[ 1 ] eq "mute" ) { - SB_PLAYER_SetSyncedVolume($hash, -1) if ($args[ 2 ] == 1); + SB_PLAYER_SetSyncedVolume($hash, -1, 0) if ($args[ 2 ] == 1); IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ?\n" ) if ($args[ 2 ] == 0); # CD 0010 end # CD 0016 start @@ -1457,7 +1568,7 @@ sub SB_PLAYER_Parse( $$ ) { # CD 0039 end } } else { - readingsBulkUpdate( $hash, "lastunknowncmd", + readingsBulkUpdate( $hash, "lastunknowncmd", $cmd . " " . join( " ", @args ) ); } @@ -1515,7 +1626,7 @@ sub SB_PLAYER_Parse( $$ ) { $args[0]=0 if($args[ 0 ] eq '?'); # CD 0050 $hash->{helper}{elapsedTime}{VAL}=$args[ 0 ]; $hash->{helper}{elapsedTime}{TS}=gettimeofday(); - readingsBulkUpdate( $hash, "currentTrackPosition",int($args[ 0 ]+0.5)); # CD 0047 + readingsBulkUpdate( $hash, "currentTrackPosition",int($args[ 0 ]+0.5)); # CD 0047 delete($hash->{helper}{saveLocked}) if (($hash->{helper}{ttsstate}==TTS_IDLE) && defined($hash->{helper}{saveLocked})); } elsif( $cmd eq "playlist_tracks" ) { readingsBulkUpdate( $hash, "playlistTracks", $args[ 0 ] ); @@ -1551,7 +1662,7 @@ sub SB_PLAYER_Parse( $$ ) { $hash->{helper}{ttsRestoreVolumeAfterStop}=ReadingsVal($name,"volumeStraight","?"); my $vol=$hash->{helper}{ttsVolume}; $vol=AttrVal( $name, "volumeLimit", 100 ) if(( $hash->{helper}{ttsVolume} > AttrVal( $name, "volumeLimit", 100 ) )&&!defined($hash->{helper}{ttsOptions}{ignorevolumelimit})); - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".$vol."\n" ); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".SB_PLAYER_FHEM2LMSVolume($hash, $vol)."\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume } } # CD 0031 end @@ -1564,11 +1675,11 @@ sub SB_PLAYER_Parse( $$ ) { # CD 0056 end Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_Parse: $name: fhemrelay ttsstopped" ); if($hash->{helper}{ttsstate}==TTS_PLAYING) { - # CD 0034 delay ttsstopped + # CD 0034 delay ttsstopped RemoveInternalTimer( "TTSStopped:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSStopped", - "TTSRestore:$name", + "TTSRestore:$name", 0 ); } } @@ -1590,7 +1701,7 @@ sub SB_PLAYER_Parse( $$ ) { # CD 0030 end # CD 0031 Lautstärke zurücksetzen if(defined($hash->{helper}{ttsRestoreVolumeAfterStop})) { - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".($hash->{helper}{ttsRestoreVolumeAfterStop})."\n" ); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".SB_PLAYER_FHEM2LMSVolume($hash, $hash->{helper}{ttsRestoreVolumeAfterStop})."\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume delete($hash->{helper}{ttsRestoreVolumeAfterStop}); } # CD 0031 end @@ -1610,24 +1721,151 @@ sub SB_PLAYER_Parse( $$ ) { # CD 0030 end } # CD 0022 end + # CD 0065 start + } elsif( $cmd eq "songinfo" ) { + my $trackid=0; + my $title=""; + my $artist=""; + my $album=""; + my $flush=0; + + #Log3( $hash, 3, "SB_PLAYER_Parse: $name: parsing songinfo: $msg" ); # CD 0072 + + foreach( @args ) { + $flush=0; + if( $_ =~ /^(id:)(-?[0-9]*)/ ) { + $trackid=$2; + # bereits bekannt -> abbrechen + if(defined($hash->{helper}{playlistInfo}{$2}) && ($trackid>0)) { + last; + } + $hash->{helper}{playlistInfo}{$trackid}{title}='no data'; + $hash->{helper}{playlistInfo}{$trackid}{artist}='-'; + $hash->{helper}{playlistInfo}{$trackid}{album}='-'; + $hash->{helper}{playlistInfo}{$trackid}{coverid}=0; + $hash->{helper}{playlistInfo}{$trackid}{duration}=0; + $hash->{helper}{playlistInfo}{$trackid}{tracknum}=0; + $hash->{helper}{playlistInfo}{$trackid}{url}='-'; + $hash->{helper}{playlistInfo}{$trackid}{artwork_url}="-" unless(defined($hash->{helper}{playlistInfo}{$trackid}{artwork_url})); # CD 0068 + next; + } elsif( $_ =~ /^(title:)(.*)/ ) { + $title=$2; + $flush=6; + } elsif( $_ =~ /^(artist:)(.*)/ ) { + $artist=$2; + $flush=5; + } elsif( $_ =~ /^(coverid:)(.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{coverid}=$2; + $flush=7; + } elsif( $_ =~ /^(remote:)(.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{remote}=1; + $flush=7; + } elsif( $_ =~ /^(duration:)(.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{duration}=$2; + $flush=7; + } elsif( $_ =~ /^(tracknum:)(.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{tracknum}=$2; + $flush=7; + } elsif( $_ =~ /^(album:)(.*)/ ) { + $album=$2; + $flush=3; + } elsif( $_ =~ /^(url:)(.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{url}=$2; + $flush=7; + } elsif( $_ =~ /^(artwork_url:)(http.*)/ ) { + $hash->{helper}{playlistInfo}{$trackid}{artwork_url}=$2; + $flush=7; + } elsif( $_ =~ /^(.*:)(.*)/ ) { + $flush=7; + } else { + $title.=" ".$_ if($title ne ""); + $album.=" ".$_ if($album ne ""); + $artist.=" ".$_ if($artist ne ""); + next; + } + if(($flush&1)&&($title ne "")) { + $hash->{helper}{playlistInfo}{$trackid}{title}=$title; + $title=""; + } + if(($flush&2)&&($artist ne "")) { + $hash->{helper}{playlistInfo}{$trackid}{artist}=$artist; + $artist=""; + } + if(($flush&4)&&($album ne "")) { + $hash->{helper}{playlistInfo}{$trackid}{album}=$album; + $album=""; + } + } + $hash->{helper}{playlistInfo}{$trackid}{title}=$title if($title ne ""); + $hash->{helper}{playlistInfo}{$trackid}{artist}=$artist if($artist ne ""); + $hash->{helper}{playlistInfo}{$trackid}{album}=$album if($album ne ""); + # TEST + # if (rand() > 0.4) {delete $hash->{helper}{playlistInfo}{$trackid}}; + # TEST + } elsif( $cmd eq "FHEMupdatePlaylistInfoDone" ) { + my $wait=1; + + my $coverbase="http://" . $hash->{SBSERVER} . "/music/"; + + my @ids=split(',',$hash->{helper}{playlistIds}); + my $ftuimedialist="["; + # [ {"Artist":"abc", "Title":"def", "Album":"yxz", "Time":"123", "File":"spotify:track:123456", "Track":"1", "Cover":"https://...." }, {"Artist":"abc", ... ] + $hash->{helper}{playlistInfoRetries}--; # CD 0076 + foreach(@ids) { + if(defined($hash->{helper}{playlistInfo}{$_})) { + $hash->{helper}{playlistInfo}{$_}{artist}=~s/\"//g; + $hash->{helper}{playlistInfo}{$_}{title}=~s/\"//g; + $hash->{helper}{playlistInfo}{$_}{album}=~s/\"//g; + $ftuimedialist.="{\"Artist\":\"".$hash->{helper}{playlistInfo}{$_}{artist}."\","; + $ftuimedialist.="\"Title\":\"".$hash->{helper}{playlistInfo}{$_}{title}."\","; + $ftuimedialist.="\"Album\":\"".$hash->{helper}{playlistInfo}{$_}{album}."\","; + $ftuimedialist.="\"Time\":\"".(int($hash->{helper}{playlistInfo}{$_}{duration}))."\","; + $ftuimedialist.="\"File\":\"".$hash->{helper}{playlistInfo}{$_}{url}."\","; + $ftuimedialist.="\"Track\":\"".$hash->{helper}{playlistInfo}{$_}{tracknum}."\","; + if(defined($hash->{helper}{playlistInfo}{$_}{artwork_url}) && ($hash->{helper}{playlistInfo}{$_}{artwork_url} ne "-")) { + $ftuimedialist.="\"Cover\":\"".$hash->{helper}{playlistInfo}{$_}{artwork_url}."\"},"; + } else { + $ftuimedialist.="\"Cover\":\"".$coverbase.$hash->{helper}{playlistInfo}{$_}{coverid}."/cover_50x50_o\"},"; + } + } else { + $ftuimedialist.="{\"Artist\":\"-\","; + $ftuimedialist.="\"Album\":\"-\","; + $ftuimedialist.="\"Time\":\"0\","; + $ftuimedialist.="\"File\":\"-\","; + $ftuimedialist.="\"Track\":\"0\","; + $ftuimedialist.="\"Cover\":\"-\"},"; + if($hash->{helper}{playlistInfoRetries}>0) { # CD 0076 + SB_PLAYER_SonginfoAddQueue($hash,$_,$wait) unless ($_==0); # CD 0072 # CD 0076 + $wait=0; + $ftuimedialist.="\"Title\":\"loading...\","; + } else { + $ftuimedialist.="\"Title\":\"no data\","; # CD 0076 + Log3( $hash, 3, "SB_PLAYER_Parse: $name: no songinfo for id $_" ); # CD 0072 + } + } + } + $ftuimedialist=~s/,$/]/; + #Log 0,$ftuimedialist; + readingsBulkUpdate( $hash, "ftuiMedialist", $ftuimedialist ); + # CD 0065 end } elsif( $cmd eq "NONE" ) { - # we shall never end up here, as cmd=NONE is used by the server for + # we shall never end up here, as cmd=NONE is used by the server for # autocreate } else { # unknown command, we push it to the last command thingy - readingsBulkUpdate( $hash, "lastunknowncmd", + readingsBulkUpdate( $hash, "lastunknowncmd", $cmd . " " . join( " ", @args ) ); } - + # and signal the end of the readings update - + if( AttrVal( $name, "donotnotify", "false" ) eq "true" ) { readingsEndUpdate( $hash, 0 ); } else { readingsEndUpdate( $hash, 1 ); } - + Log3( $hash, 5, "SB_PLAYER_Parse: $name: leaving" ); return( $name ); @@ -1664,9 +1902,9 @@ sub SB_PLAYER_tcb_TTSDelay( $ ) { IOWrite( $hash, "$hash->{PLAYERMAC} play\n" ); # CD 0038 Timeout hinzugefügt RemoveInternalTimer( "TimeoutTTSWaitForPlay:$name"); - InternalTimer( gettimeofday() + 10.00, + InternalTimer( gettimeofday() + 10.00, "SB_PLAYER_tcb_TimeoutTTSWaitForPlay", - "TimeoutTTSWaitForPlay:$name", + "TimeoutTTSWaitForPlay:$name", 0 ); # CD 0038 end } @@ -1740,9 +1978,9 @@ sub SB_PLAYER_tcb_TTSStartAfterPowerOn( $ ) { SB_PLAYER_PrepareTalk($hash); SB_PLAYER_LoadTalk($hash); } else { - InternalTimer( gettimeofday() + 1, + InternalTimer( gettimeofday() + 1, "SB_PLAYER_tcb_TTSStartAfterPowerOn", - "TTSStartAfterPowerOn:$name", + "TTSStartAfterPowerOn:$name", 0 ); $hash->{helper}{playerStatusOKCounter}++; } @@ -1763,9 +2001,9 @@ sub SB_PLAYER_TTSStopped($) { } else { SB_PLAYER_SetTTSState($hash,TTS_STOP,1,0); RemoveInternalTimer( "TTSRestore:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSRestore", - "TTSRestore:$name", + "TTSRestore:$name", 0 ); } } @@ -1776,15 +2014,15 @@ sub SB_PLAYER_TTSStopped($) { # ---------------------------------------------------------------------------- sub SB_PLAYER_Undef( $$$ ) { my ($hash, $arg) = @_; - + Log3( $hash, 5, "SB_PLAYER_Undef: called" ); - + RemoveInternalTimer( $hash ); - # to be reviewed if that works. + # to be reviewed if that works. # check for uc() # what is $hash->{DEF}? - delete $modules{SB_PLAYER}{defptr}{uc($hash->{DEF})}; + delete $modules{SB_PLAYER}{defptr}{$hash->{FHEMUID}}; # CD 0071 uc($hash->{DEF}) durch $hash->{FHEMUID} ersetzt return( undef ); } @@ -1794,7 +2032,7 @@ sub SB_PLAYER_Undef( $$$ ) { # ---------------------------------------------------------------------------- sub SB_PLAYER_Shutdown( $$ ) { my ($hash, $dev) = @_; - + RemoveInternalTimer( $hash ); Log3( $hash, 5, "SB_PLAYER_Shutdown: called" ); @@ -1809,7 +2047,7 @@ sub SB_PLAYER_Shutdown( $$ ) { # ---------------------------------------------------------------------------- sub SB_PLAYER_Get( $@ ) { my ($hash, @a) = @_; - + my $name = $hash->{NAME}; Log3( $hash, 4, "SB_PLAYER_Get: called with @a" ); @@ -1822,13 +2060,13 @@ sub SB_PLAYER_Get( $@ ) { #my $name = shift( @a ); shift( @a ); # name - my $cmd = shift( @a ); + my $cmd = shift( @a ); if( $cmd eq "?" ) { - my $res = "Unknown argument ?, choose one of " . + my $res = "Unknown argument ?, choose one of " . "volume " . $hash->{FAVSET} . " savedStates alarmPlaylists"; # CD 0045 alarmPlaylists hinzugefügt return( $res ); - + } elsif( ( $cmd eq "volume" ) || ( $cmd eq "volumeStraight" ) ) { return( scalar( ReadingsVal( "$name", "volumeStraight", 25 ) ) ); @@ -1859,7 +2097,7 @@ sub SB_PLAYER_Get( $@ ) { my $msg = "SB_PLAYER_Get: $name: unkown argument"; Log3( $hash, 5, $msg ); return( $msg ); - } + } return( undef ); } @@ -1871,9 +2109,9 @@ sub SB_PLAYER_Get( $@ ) { sub SB_PLAYER_GetTTSDelay( $ ) { my ( $hash ) = @_; my $name = $hash->{NAME}; - + # todo synced players - + if(defined($hash->{helper}{ttsDelay})) { if(ReadingsVal($name,"power","x") eq "on") { return $hash->{helper}{ttsDelay}{PowerIsOn} @@ -1912,18 +2150,25 @@ sub SB_PLAYER_Notify( $$ ) { my $devName = $dev_hash->{NAME}; # Device that created the events if ($dev_hash->{NAME} eq "global" && grep (m/^INITIALIZED$|^REREADCFG$/,@{$dev_hash->{CHANGED}})){ + # CD 0077 unbenutzte Attribute entfernen + $modules{$hash->{TYPE}}{AttrList} =~ s/serverautoon.//; + $modules{$hash->{TYPE}}{AttrList} =~ s/idismac.//; } # CD 0036 start if( grep(m/^SAVE$|^SHUTDOWN$/, @{$dev_hash->{CHANGED}}) ) { # CD 0043 auch bei SHUTDOWN speichern + # CD 0077 unbenutzte Attribute entfernen + delete($attr{$name}{serverautoon}) if(defined( $attr{$name}{serverautoon})); + delete($attr{$name}{idismac}) if(defined( $attr{$name}{idismac})); + SB_PLAYER_SavePlayerStates($hash) if($SB_PLAYER_hasDataDumper==1); } # CD 0036 end - + # CD 0033 start if(defined($hash->{helper}{text2speech}{name}) && ($hash->{helper}{text2speech}{name} eq $devName)) { $hash->{helper}{ttsExtstate}=TTS_IDLE if(!defined($hash->{helper}{ttsExtstate})); - + if(($hash->{helper}{ttsstate}==TTS_TEXT2SPEECH_ACTIVE)||($hash->{helper}{ttsExtstate}==TTS_EXT_TEXT2SPEECH_ACTIVE)) { foreach my $line (@{$dev_hash->{CHANGED}}) { my @args=split(' ',$line); @@ -1933,9 +2178,9 @@ sub SB_PLAYER_Notify( $$ ) { } elsif($args[1] eq 'ttsdone') { RemoveInternalTimer( "StartTalk:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_StartT2STalk", - "StartTalk:$name", + "StartTalk:$name", 0 ); } } @@ -1971,7 +2216,7 @@ sub SB_PLAYER_Set( $@ ) { Log3( $hash, 5, "SB_PLAYER_Set: called with $cmd"); # check if we have received a command - if( !defined( $cmd ) ) { + if( !defined( $cmd ) ) { my $msg = "$name: set needs at least one parameter"; Log3( $hash, 3, $msg ); return( $msg ); @@ -1980,21 +2225,25 @@ sub SB_PLAYER_Set( $@ ) { # now parse the commands if( $cmd eq "?" ) { # this one should give us a drop down list - my $res = "Unknown argument ?, choose one of " . - "on off stop:noArg play:noArg pause:noArg " . + my $res = "Unknown argument ?, choose one of " . + "on off stop:noArg play:noArg pause:noArg " . "save " . # CD 0014 # CD 0036 removed :noArg - "volume:slider,0,1,100 " . - "volumeStraight:slider,0,1,100 " . - "volumeUp:noArg volumeDown:noArg " . - "mute:noArg repeat:off,one,all show statusRequest:noArg " . + "volume:slider,0,1,100 " . + "volumeStraight:slider,0,1,100 " . + "volumeUp:noArg volumeDown:noArg " . + "mute:noArg repeat:off,one,all show statusRequest:noArg " . "shuffle:off,song,album next:noArg prev:noArg playlist sleep " . # CD 0017 song und album hinzugefügt # CD 0052 shuffle on entfernt "allalarms:enable,disable,statusRequest,delete,add " . # CD 0015 alarm1 alarm2 entfernt "alarmsSnooze:slider,0,1,30 alarmsTimeout:slider,0,5,90 alarmsDefaultVolume:slider,0,1,100 alarmsFadeIn:on,off alarmsEnabled:on,off " . # CD 0016, von MM übernommen, Namen geändert - "cliraw talk sayText " . # CD 0014 sayText hinzugefügt + "cliraw talk sayText " . # CD 0014 sayText hinzugefügt "unsync:noArg " . "currentTrackPosition " . # CD 0047 hinzugefügt "snooze:noArg " . # CD 0052 hinzugefügt - "resetTTS:noArg "; # CD 0028 hinzugefügt + "resetTTS:noArg " . # CD 0028 hinzugefügt + "updateFTUImedialist:noArg " . # CD 0065 hinzugefügt + "clearFTUIcache:noArg " . # CD 0065 hinzugefügt + "track " . # CD 0065 hinzugefügt + "tracknumber "; # CD 0072 hinzugefügt # add the favorites $res .= $hash->{FAVSET} . ":-," . $hash->{FAVSTR} . " "; # CD 0014 '-' hinzugefügt # add the syncmasters @@ -2005,7 +2254,7 @@ sub SB_PLAYER_Set( $@ ) { my $out=""; if (defined($hash->{helper}{savedPlayerState})) { foreach my $pl ( keys %{$hash->{helper}{savedPlayerState}} ) { - $out.=$pl."," unless ($pl=~/xxTTSxx/); + $out.=$pl."," unless (($pl=~/xxTTSxx/)||($pl=~/^xxx_sss_.*/)); # CD 0061 xxx_sss_ hinzugefügt } $out=~s/,$//; } @@ -2031,10 +2280,10 @@ sub SB_PLAYER_Set( $@ ) { # return "$name: player is not available"; # } # CD 0038 end - + my $updateReadingsOnSet=AttrVal($name, "updateReadingsOnSet", false); # CD 0017 my $donotnotify=AttrVal($name, "donotnotify", "true"); # CD 0017 # CD 0028 added " - + # as we have some other command, we need to turn on the server #if( AttrVal( $name, "serverautoon", "true" ) eq "true" ) { #SB_PLAYER_ServerTurnOn( $hash ); @@ -2081,15 +2330,15 @@ sub SB_PLAYER_Set( $@ ) { # CD 0050 end } - } elsif( ( $cmd eq "next" ) || ( $cmd eq "NEXT" ) || ( $cmd eq "Next" ) || + } elsif( ( $cmd eq "next" ) || ( $cmd eq "NEXT" ) || ( $cmd eq "Next" ) || ( $cmd eq "channelUp" ) || ( $cmd eq "CHANNELUP" ) ) { IOWrite( $hash, "$hash->{PLAYERMAC} playlist jump %2B1\n" ); - } elsif( ( $cmd eq "prev" ) || ( $cmd eq "PREV" ) || ( $cmd eq "Prev" ) || + } elsif( ( $cmd eq "prev" ) || ( $cmd eq "PREV" ) || ( $cmd eq "Prev" ) || ( $cmd eq "channelDown" ) || ( $cmd eq "CHANNELDOWN" ) ) { IOWrite( $hash, "$hash->{PLAYERMAC} playlist jump %2D1\n" ); - } elsif( ( $cmd eq "volume" ) || ( $cmd eq "VOLUME" ) || + } elsif( ( $cmd eq "volume" ) || ( $cmd eq "VOLUME" ) || ( $cmd eq "Volume" ) ||( $cmd eq "volumeStraight" ) ) { if(( @arg != 1 )&&( @arg != 2 )) { my $msg = "SB_PLAYER_Set: no arguments for Vol given."; @@ -2099,51 +2348,57 @@ sub SB_PLAYER_Set( $@ ) { # set the volume to the desired level. Needs to be 0..100 # no error checking here, as the server does this if( $arg[ 0 ] <= AttrVal( $name, "volumeLimit", 100 ) ) { - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $arg[ 0 ]\n" ); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".SB_PLAYER_FHEM2LMSVolume($hash, $arg[ 0 ])."\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume # CD 0007 - SB_PLAYER_SetSyncedVolume($hash,$arg[0]) if (!defined($arg[1])); + SB_PLAYER_SetSyncedVolume($hash,$arg[0],1) if (!defined($arg[1])); } else { - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " . - AttrVal( $name, "volumeLimit", 50 ) . "\n" ); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " . + SB_PLAYER_FHEM2LMSVolume($hash, AttrVal( $name, "volumeLimit", 50 )) . "\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume # CD 0007 - SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 )) if (!defined($arg[1])); + SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 ),1) if (!defined($arg[1])); } } elsif( $cmd eq $hash->{FAVSET} ) { if ($arg[0] ne '-') { # CD 0014 if( defined( $hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{ID} ) ) { my $fid = $hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{ID}; - IOWrite( $hash, "$hash->{PLAYERMAC} favorites playlist " . - "play item_id:$fid\n" ); + + if($hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{SOURCE} eq 'LMS') { # CD 0070 + IOWrite( $hash, "$hash->{PLAYERMAC} favorites playlist " . + "play item_id:$fid\n" ); + } else { + IOWrite( $hash, "$hash->{PLAYERMAC} $hash->{helper}{SB_PLAYER_Favs}{$arg[0]}{SOURCE} playlist play " . + "item_id:$fid\n" ); + } $hash->{FAVSELECT} = $arg[ 0 ]; readingsSingleUpdate( $hash, "$hash->{FAVSET}", "$arg[ 0 ]", 1 ); # SB_PLAYER_GetStatus( $hash ); # CD 0021 deaktiviert, zu früh } } # CD 0014 - } elsif( ( $cmd eq "volumeUp" ) || ( $cmd eq "VOLUMEUP" ) || + } elsif( ( $cmd eq "volumeUp" ) || ( $cmd eq "VOLUMEUP" ) || ( $cmd eq "VolumeUp" ) ) { # increase volume - if( ( ReadingsVal( $name, "volumeStraight", 50 ) + - AttrVal( $name, "volumeStep", 10 ) ) <= + if( ( ReadingsVal( $name, "volumeStraight", 50 ) + + AttrVal( $name, "volumeStep", 10 ) ) <= AttrVal( $name, "volumeLimit", 100 ) ) { my $volstr = sprintf( "+%02d", AttrVal( $name, "volumeStep", 10 ) ); IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $volstr\n" ); # CD 0007 - SB_PLAYER_SetSyncedVolume($hash,$volstr); + #SB_PLAYER_SetSyncedVolume($hash,$volstr); # CD 0065 deaktiviert, auf Rückmeldung warten } else { - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " . - AttrVal( $name, "volumeLimit", 50 ) . "\n" ); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume " . + SB_PLAYER_FHEM2LMSVolume($hash, AttrVal( $name, "volumeLimit", 50 )) . "\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume # CD 0007 - SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 )); + SB_PLAYER_SetSyncedVolume($hash,AttrVal( $name, "volumeLimit", 50 ),1); } - } elsif( ( $cmd eq "volumeDown" ) || ( $cmd eq "VOLUMEDOWN" ) || + } elsif( ( $cmd eq "volumeDown" ) || ( $cmd eq "VOLUMEDOWN" ) || ( $cmd eq "VolumeDown" ) ) { my $volstr = sprintf( "-%02d", AttrVal( $name, "volumeStep", 10 ) ); IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $volstr\n" ); # CD 0007 - SB_PLAYER_SetSyncedVolume($hash,$volstr); + #SB_PLAYER_SetSyncedVolume($hash,$volstr); # CD 0065 deaktiviert, auf Rückmeldung warten } elsif( ( $cmd eq "mute" ) || ( $cmd eq "MUTE" ) || ( $cmd eq "Mute" ) ) { IOWrite( $hash, "$hash->{PLAYERMAC} mixer muting toggle\n" ); @@ -2162,7 +2417,7 @@ sub SB_PLAYER_Set( $@ ) { IOWrite( $hash, "$hash->{PLAYERMAC} power 0\n" ); } - } elsif( ( $cmd eq "repeat" ) || ( $cmd eq "REPEAT" ) || + } elsif( ( $cmd eq "repeat" ) || ( $cmd eq "REPEAT" ) || ( $cmd eq "Repeat" ) ) { if( @arg != 1 ) { my $msg = "SB_PLAYER_Set: no arguments for repeat given."; @@ -2182,9 +2437,9 @@ sub SB_PLAYER_Set( $@ ) { my $msg = "SB_PLAYER_Set: unknown argument for repeat given."; Log3( $hash, 3, $msg ); return( $msg ); - } - - } elsif( ( $cmd eq "shuffle" ) || ( $cmd eq "SHUFFLE" ) || + } + + } elsif( ( $cmd eq "shuffle" ) || ( $cmd eq "SHUFFLE" ) || ( $cmd eq "Shuffle" ) ) { if( @arg != 1 ) { my $msg = "SB_PLAYER_Set: no arguments for shuffle given."; @@ -2206,10 +2461,10 @@ sub SB_PLAYER_Set( $@ ) { my $msg = "SB_PLAYER_Set: unknown argument for shuffle given."; Log3( $hash, 3, $msg ); return( $msg ); - } + } - } elsif( ( $cmd eq "show" ) || - ( $cmd eq "SHOW" ) || + } elsif( ( $cmd eq "show" ) || + ( $cmd eq "SHOW" ) || ( $cmd eq "Show" ) ) { # set show line1:text line2:text duration:ss my $v = join( " ", @arg ); @@ -2222,8 +2477,8 @@ sub SB_PLAYER_Set( $@ ) { my $cmdstr = "$hash->{PLAYERMAC} display $line1 $line2 $duration\n"; IOWrite( $hash, $cmdstr ); - } elsif( ( $cmd eq "talk" ) || - ( $cmd eq "TALK" ) || + } elsif( ( $cmd eq "talk" ) || + ( $cmd eq "TALK" ) || ( $cmd eq "talk" ) || ( lc($cmd) eq "saytext" ) ) { # CD 0014 hinzugefügt @@ -2308,7 +2563,7 @@ sub SB_PLAYER_Set( $@ ) { # CD 0033 Unterstützung für Dateien und URLs hinzugefügt, teilweise aus 00_SONOS übernommen my $targetSpeakMP3FileDir = AttrVal( $name, "ttsMP3FileDir", "" ); # CD 0033 my $filename; # CD 0038 - + if (length($ttstext)>0) { my @words=split(' ',$ttstext); for my $w (@words) { @@ -2316,7 +2571,7 @@ sub SB_PLAYER_Set( $@ ) { if ($w =~ m/\|(.*)\|/) { push(@textlines,$tl) if($tl ne ''); $tl=''; - + $filename = $1; $filename = $targetSpeakMP3FileDir.'/'.$filename if ($filename !~ m/^(\/|[a-z]:)/i); $filename = $filename.'.mp3' if ($filename !~ m/\.mp3$/i); @@ -2330,7 +2585,7 @@ sub SB_PLAYER_Set( $@ ) { push(@textlines,$tl) if($tl ne ''); $tl=''; - + $filename = $targetSpeakMP3FileDir.'/'.$filename if ($filename !~ m/^(\/|[a-z]:)/i); $filename = $filename.'.mp3' if ($filename !~ m/\.mp3$/i); push(@textlines, '|'.$filename.'|'); @@ -2363,16 +2618,16 @@ sub SB_PLAYER_Set( $@ ) { RemoveInternalTimer( "TimeoutTTSWaitForPowerOn:$name"); if($hash->{helper}{playerStatusOK}==1) { # CD 0042 IOWrite( $hash, "$hash->{PLAYERMAC} power 1\n" ); - InternalTimer( gettimeofday() + 5.00, + InternalTimer( gettimeofday() + 5.00, "SB_PLAYER_tcb_TimeoutTTSWaitForPowerOn", - "TimeoutTTSWaitForPowerOn:$name", + "TimeoutTTSWaitForPowerOn:$name", 0 ); $hash->{helper}{ttsPowerWasOff}=1; # CD 0042 start } else { - InternalTimer( gettimeofday() + 10.00, + InternalTimer( gettimeofday() + 10.00, "SB_PLAYER_tcb_TimeoutTTSWaitForPowerOn", - "TimeoutTTSWaitForPowerOn:$name", + "TimeoutTTSWaitForPowerOn:$name", 0 ); } # CD 0042 end @@ -2381,7 +2636,7 @@ sub SB_PLAYER_Set( $@ ) { SB_PLAYER_PrepareTalk($hash); } } else { - + } for my $outstr (@textlines) { if ($outstr =~ m/\|(.*)\|/) { # CD 0033 @@ -2395,10 +2650,10 @@ sub SB_PLAYER_Set( $@ ) { Log3($hash, defined($hash->{helper}{ttsOptions}{debug})?0:6,"SB_PLAYER_Set: $name: string is not utf-8, using iso-8859-1"); $outstr=encode('utf-8',decode("iso-8859-1",$outstr)); } - + $outstr = uri_escape( $outstr ); #$outstr = uri_escape( $outstr ) if defined($hash->{helper}{ttsOptions}{doubleescape}); # CD 0059 # CD 0060 zu spät nach oben verschoben - + # CD 0045 my $ttslink=AttrVal( $name, "ttslink", "" ); if(defined($ttslink)) { @@ -2419,7 +2674,7 @@ sub SB_PLAYER_Set( $@ ) { $ttslink.="&tl=" if($ttslink !~ m//); $ttslink.="&q="; } - + $ttslink =~ s//$lang/; $ttslink =~ s//$outstr/; @@ -2429,19 +2684,19 @@ sub SB_PLAYER_Set( $@ ) { SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,0); } else { $ttslink =~ s//$apikey/; - + Log3($hash, defined($hash->{helper}{ttsOptions}{debug})?0:6,"SB_PLAYER_Set: $name: add to ttsqueue: $ttslink"); # CD 0036 push(@{$hash->{helper}{ttsqueue}},$ttslink); } } } } - + SB_PLAYER_LoadTalk($hash) if($hash->{helper}{ttsstate}==TTS_LOADPLAYLIST); # CD 0033 # CD 0038 auf TTS_LOADPLAYLIST prüfen # CD 0028 end - } elsif( ( $cmd eq "playlist" ) || - ( $cmd eq "PLAYLIST" ) || + } elsif( ( $cmd eq "playlist" ) || + ( $cmd eq "PLAYLIST" ) || ( $cmd eq "Playlist" ) ) { #if( ( @arg != 2 ) && ( @arg != 3 ) ) { # CD 0014 deaktiviert if( @arg < 2) { # CD 0014 @@ -2456,13 +2711,13 @@ sub SB_PLAYER_Set( $@ ) { Log3( $hash, 5, "SB_PLAYER_Set($name): playlists command = $arg[ 0 ] param = $outstr" ); if( $arg[ 0 ] eq "track" ) { - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . "track.titlesearch:$outstr\n" ); } elsif( $arg[ 0 ] eq "album" ) { - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . "album.titlesearch:$outstr\n" ); } elsif( $arg[ 0 ] eq "artist" ) { - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . "contributor.namesearch:$outstr\n" ); # CD 0014 'titlesearch' durch 'namesearch' ersetzt } elsif( $arg[ 0 ] eq "play" ) { IOWrite( $hash, "$hash->{PLAYERMAC} playlist play $outstr\n" ); @@ -2473,13 +2728,13 @@ sub SB_PLAYER_Set( $@ ) { IOWrite( $hash, "$hash->{PLAYERMAC} playlist insert $outstr\n" ); # CD 0038 end } elsif( $arg[ 0 ] eq "year" ) { - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . "track.year:$outstr\n" ); } elsif( $arg[ 0 ] eq "genre" ) { - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . "genre.namesearch:$outstr\n" ); #} elsif( $arg[ 0 ] eq "comment" ) { # CD 0014 funktioniert nicht - # IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . + # IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadtracks " . # "comments.value:$outstr\n" ); # CD 0038 loadalbum wieder hergestellt und überarbeitet } else { @@ -2507,7 +2762,7 @@ sub SB_PLAYER_Set( $@ ) { $genre=uri_escape(decode('utf-8',$genre)) if($genre ne '*'); $album=uri_escape(decode('utf-8',$album)) if($album ne '*'); $artist=uri_escape(decode('utf-8',$artist)) if($artist ne '*'); - IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadalbum " . + IOWrite( $hash, "$hash->{PLAYERMAC} playlist loadalbum " . "$genre $artist $album\n" ); } # CD 0038 end @@ -2587,7 +2842,7 @@ sub SB_PLAYER_Set( $@ ) { my $msg = "SB_PLAYER_Set: please use hh:mm:ss for sleep time."; Log3( $hash, 3, $msg ); return( $msg ); - } + } my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ]; IOWrite( $hash, "$hash->{PLAYERMAC} sleep $secs\n" ); return( undef ); @@ -2597,7 +2852,7 @@ sub SB_PLAYER_Set( $@ ) { # write raw messages to the CLI interface per player my $v = join( " ", @arg ); - Log3( $hash, 5, "SB_PLAYER_Set: cliraw: $v " ); + Log3( $hash, 5, "SB_PLAYER_Set: cliraw: $v " ); IOWrite( $hash, "$hash->{PLAYERMAC} $v\n" ); return( undef ); @@ -2625,12 +2880,12 @@ sub SB_PLAYER_Set( $@ ) { IOWrite( $hash, "$hash->{PLAYERMAC} sync -\n" ); # CD 0028 start if($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) { - SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,0); + SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,0); # CD 0066 single statt bulk update } # CD 0028 end } # CD 0018 end - # CD 0018 Synchronisation mehrerer Player + # CD 0018 Synchronisation mehrerer Player if(( @arg == 1 ) || ( @arg == 2)) { my $msg; my $dev; @@ -2657,11 +2912,11 @@ sub SB_PLAYER_Set( $@ ) { if(defined($mac)) { # CD 0038 wenn asSlave angegeben ist, wird der erste Player zur Gruppe des 2. hinzugefügt if((@arg == 2) && ($arg[1] eq "asSlave")) { - IOWrite( $hash, "$mac sync " . + IOWrite( $hash, "$mac sync " . "$hash->{PLAYERMAC}\n" ); } else { # CD 0038 end - IOWrite( $hash, "$hash->{PLAYERMAC} sync " . + IOWrite( $hash, "$hash->{PLAYERMAC} sync " . "$mac\n" ); } $doGetStatus=1; @@ -2675,21 +2930,25 @@ sub SB_PLAYER_Set( $@ ) { SB_PLAYER_GetStatus( $hash ); # CD 0028 start if($hash->{helper}{ttsstate}==TTS_SYNCGROUPACTIVE) { - SB_PLAYER_SetTTSState($hash,TTS_IDLE,1,0); + SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,0); # CD 0066 single statt bulk update } # CD 0028 end } elsif( $cmd eq "playlists" ) { if( @arg == 1 ) { my $msg; if( defined( $hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID} ) ) { - $msg = "$hash->{PLAYERMAC} playlistcontrol cmd:load " . - "playlist_id:$hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID}"; - Log3( $hash, 5, "SB_PLAYER_Set($name): playlists command = " . + if($hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{SOURCE} eq 'LMS') { # CD 0070 + $msg = "$hash->{PLAYERMAC} playlistcontrol cmd:load " . + "playlist_id:$hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID}"; + } else { + $msg = "$hash->{PLAYERMAC} $hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{SOURCE} playlist play " . + "item_id:$hash->{helper}{SB_PLAYER_Playlists}{$arg[0]}{ID}"; + } + Log3( $hash, 5, "SB_PLAYER_Set($name): playlists command = " . $msg . " ........ with $arg[0]" ); IOWrite( $hash, $msg . "\n" ); readingsSingleUpdate( $hash, "playlists", "$arg[ 0 ]", 1 ); SB_PLAYER_GetStatus( $hash ); - } else { $msg = "SB_PLAYER_Set: no name for playlist defined."; Log3( $hash, 3, $msg ); @@ -2702,6 +2961,7 @@ sub SB_PLAYER_Set( $@ ) { } } elsif( $cmd eq "resetTTS" ) { delete($hash->{helper}{saveLocked}) if (defined($hash->{helper}{saveLocked})); # CD 0056 + delete $hash->{helper}{sgTalkActive} if defined($hash->{helper}{sgTalkActive}); # CD 0063 SB_PLAYER_SetTTSState($hash,TTS_IDLE,0,1); # CD 0047 start } elsif( lc($cmd) eq "currenttrackposition" ) { @@ -2713,14 +2973,40 @@ sub SB_PLAYER_Set( $@ ) { } elsif( lc($cmd) eq "snooze" ) { IOWrite( $hash, "$hash->{PLAYERMAC} jivealarm snooze:1\n" ); # CD 0052 end + # CD 0065 start + } elsif( $cmd eq "updateFTUImedialist" ) { + delete($hash->{SONGINFOQUEUE}) if(defined($hash->{SONGINFOQUEUE})); # CD 0072 + + if(defined($hash->{helper}{playlistIds})) { + $hash->{helper}{playlistInfoRetries}=5; # CD 0076 + my @ids=split(',',$hash->{helper}{playlistIds}); + foreach(@ids) { + IOWrite( $hash, $hash->{PLAYERMAC}." songinfo 0 100 track_id:".$_." tags:acdltuxNK\n" ) unless(defined($hash->{helper}{playlistInfo}{$_}) && !defined($hash->{helper}{playlistInfo}{$_}{remote}) || ($_==0)); # CD 0076 id 0 ignorieren + } + IOWrite( $hash, $hash->{PLAYERMAC}." FHEMupdatePlaylistInfoDone\n" ); + } + } elsif( $cmd eq "clearFTUIcache" ) { + delete $hash->{helper}{playlistInfo} if defined($hash->{helper}{playlistInfo}); + } elsif(( $cmd eq "track" ) || ( $cmd eq "tracknumber" )) { + if( @arg != 1 ) { + my $msg = "SB_PLAYER_Set: no arguments for track given."; + Log3( $hash, 3, $msg ); + return( $msg ); + } + if($arg[0]=~/^\d+$/) { + IOWrite( $hash, $hash->{PLAYERMAC}." playlist index ".($arg[0]-1)."\n" ); + } else { + IOWrite( $hash, $hash->{PLAYERMAC}." playlist index ".$arg[0]."\n" ); + } + # CD 0065 end } else { my $msg = "SB_PLAYER_Set: unsupported command given"; Log3( $hash, 3, $msg ); return( $msg ); } - + return( undef ); - + } # CD 0033 hinzugefügt @@ -2772,12 +3058,12 @@ sub SB_PLAYER_LoadTalk($) { sub SB_PLAYER_PrepareTalk($) { # kein readingsBulkUpdate # aktuellen Stand abspeichern, playlist löschen, Lautstärke setzen - + my ( $hash ) = @_; my $name = $hash->{NAME}; # talk ist nicht aktiv - if(!defined($hash->{helper}{ttsOptions}{nosaverestore})) { + if(!defined($hash->{helper}{ttsOptions}{nosaverestore}) && !defined($hash->{helper}{sgTalkActive})) { # CD 0063 sgTalkActive hinzugefügt SB_PLAYER_SetTTSState($hash,TTS_SAVE,0,0); SB_PLAYER_Save( $hash, "xxTTSxx" ) if(!defined($hash->{helper}{saveLocked})); } @@ -2788,8 +3074,8 @@ sub SB_PLAYER_PrepareTalk($) { SB_PLAYER_SetTTSState($hash,TTS_SETVOLUME,0,0); my $vol=$hash->{helper}{ttsVolume}; $vol=AttrVal( $name, "volumeLimit", 100 ) if(( $hash->{helper}{ttsVolume} > AttrVal( $name, "volumeLimit", 100 ) )&&!defined($hash->{helper}{ttsOptions}{ignorevolumelimit})); # CD 0031 - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".$vol."\n" ); - SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{ttsVolume}); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".SB_PLAYER_FHEM2LMSVolume($hash, $vol)."\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume + SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{ttsVolume},1); } SB_PLAYER_SetTTSState($hash,TTS_LOADPLAYLIST,0,0); } @@ -2803,7 +3089,7 @@ sub SB_PLAYER_PrepareTalk($) { sub SB_PLAYER_Recall($$) { my ( $hash, $arg ) = @_; # CD 0036 my $name = $hash->{NAME}; - + # CD 0036 start my $del=0; my $delonly=0; @@ -2814,7 +3100,7 @@ sub SB_PLAYER_Recall($$) { my $statename; my @args=split " ",$arg; - + if(defined($args[0])) { $statename=$args[0]; } else { @@ -2830,12 +3116,12 @@ sub SB_PLAYER_Recall($$) { $forceplay=1 if($opt=~ m/^play$/); $forcestop=1 if($opt=~ m/^stop$/); } - + $forceoff=0 if($forceon==1); $forcestop=0 if($forceplay==1); - + # CD 0036 end - + # wurde überhaupt etwas gespeichert ? if(defined($hash->{helper}{savedPlayerState}) && defined($hash->{helper}{savedPlayerState}{$statename})) { if($delonly==0) { @@ -2880,8 +3166,8 @@ sub SB_PLAYER_Recall($$) { } } if ($hash->{helper}{savedPlayerState}{$statename}{volumeStraight} ne '?') { - IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume $hash->{helper}{savedPlayerState}{$statename}{volumeStraight}\n" ); - SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{savedPlayerState}{$statename}{volumeStraight}); + IOWrite( $hash, "$hash->{PLAYERMAC} mixer volume ".SB_PLAYER_FHEM2LMSVolume($hash, $hash->{helper}{savedPlayerState}{$statename}{volumeStraight})."\n" ); # CD 0065 SB_PLAYER_FHEM2LMSVolume + SB_PLAYER_SetSyncedVolume($hash,$hash->{helper}{savedPlayerState}{$statename}{volumeStraight},1); } if ($hash->{helper}{savedPlayerState}{$statename}{repeat} ne ReadingsVal($name,"repeat","?")) { if( $hash->{helper}{savedPlayerState}{$statename}{repeat} eq "off" ) { @@ -2940,12 +3226,38 @@ sub SB_PLAYER_Recall($$) { } } +# CD 0062 start +sub SB_PLAYER_tcb_TriggerTTSDone($) { + my($in ) = shift; + my(undef,$name) = split(':',$in); + my $hash = $defs{$name}; + + DoTrigger($name,"ttsdone"); +} +# CD 0062 end + +# CD 0063 start +sub SB_PLAYER_tcb_TriggerPlaylistStop($) { + my($in ) = shift; + my(undef,$name) = split(':',$in); + my $hash = $defs{$name}; + + $hash->{helper}{noStopEventUntil}=0 unless defined($hash->{helper}{noStopEventUntil}); + + if ((ReadingsVal($name,"playStatus","x") eq "stopped")&&(time()>$hash->{helper}{noStopEventUntil})&&($hash->{helper}{ttsstate}==TTS_IDLE)) { + DoTrigger($name,"playlistStop"); + } +} +# CD 0063 end + sub SB_PLAYER_SetTTSState($$$$) { my ( $hash, $state, $bulk, $broadcast ) = @_; my $name = $hash->{NAME}; return if($state eq $hash->{helper}{ttsstate}); - + + my $oldstate=$hash->{helper}{ttsstate}; # CD 0062 + $hash->{helper}{ttsstate}=$state; Log3( $hash, defined($hash->{helper}{ttsOptions}{debug})?0:6, "SB_PLAYER_SetTTSState: $name: ttsstate: ".$ttsstates{$hash->{helper}{ttsstate}} ); if($bulk==1) { @@ -2968,11 +3280,24 @@ sub SB_PLAYER_SetTTSState($$$$) { } delete($hash->{helper}{ttsqueue}) if(defined($hash->{helper}{ttsqueue}) && ($state==TTS_IDLE)); delete($hash->{helper}{ttsPowerWasOff}) if (defined($hash->{helper}{ttsPowerWasOff}) && ($state==TTS_IDLE)); + + # CD 0062 start + if(($state==TTS_IDLE)&&($oldstate!=TTS_IDLE)) { + if(defined($hash->{helper}{ttsOptions}{eventondone})) { + RemoveInternalTimer( "TriggerTTSDone:$name"); + InternalTimer( gettimeofday() + 0.5, + "SB_PLAYER_tcb_TriggerTTSDone", + "TriggerTTSDone:$name", + 0 ); + } + IOWrite( $hash, "$name fhemrelay ttsdone" ); + } + # CD 0062 end } # ---------------------------------------------------------------------------- # save player state -# +# # CD 0036 added $statename # ---------------------------------------------------------------------------- sub SB_PLAYER_Save($$) { @@ -3008,7 +3333,7 @@ sub SB_PLAYER_Save($$) { Log3( $hash, 0, "SB_PLAYER_Save: $name: saving {helper}{playlistIds}: ".$hash->{helper}{playlistIds}) if(defined($hash->{helper}{playlistIds})); } # CD 0036 - + # CD 0036, alte Version, deaktivert # --------------------------------- if(1==0) { @@ -3079,7 +3404,7 @@ sub SB_PLAYER_Alarm( $$@ ) { #if( ( $n != 1 ) && ( $n != 2 ) ) { # Log3( $hash, 1, "SB_PLAYER_Alarm: $name: wrong ID given. Must be 1|2" ); # return; - #} + #} my $id = ReadingsVal( "$name", "alarm".$n."_id", "none" ); # CD 0015 angepasst my $updateReadingsOnSet=AttrVal($name, "updateReadingsOnSet", false); # CD 0017 @@ -3094,14 +3419,14 @@ sub SB_PLAYER_Alarm( $$@ ) { Log3( $hash, 3, $msg ); return( $msg ); } - + if( $id ne "none" ) { IOWrite( $hash, "$hash->{PLAYERMAC} alarm delete id:$id\n" ); # CD 0020 'id' fehlt # readingsSingleUpdate( $hash, "alarmid$n", "none", 0 ); # CD 0015 deaktiviert } - + my $dow = SB_PLAYER_CheckWeekdays($arg[ 1 ]); # CD 0016 hinzugefügt - + # split the time string up my @buf = split( ":", $arg[ 2 ] ); $buf[ 2 ] = 0 if( scalar( @buf ) == 2 ); # CD 0016, von MM übernommen, geändert @@ -3109,10 +3434,10 @@ sub SB_PLAYER_Alarm( $$@ ) { my $msg = "SB_PLAYER_Set: please use hh:mm:ss for alarm time."; Log3( $hash, 3, $msg ); return( $msg ); - } + } my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ]; - - $cmdstr = "$hash->{PLAYERMAC} alarm add dow:$dow repeat:0 enabled:1"; + + $cmdstr = "$hash->{PLAYERMAC} alarm add dow:$dow repeat:0 enabled:1"; if( defined( $arg[ 3 ] ) ) { # CD 0015 start my $url=join( " ", @arg[3..$#arg]); @@ -3129,7 +3454,7 @@ sub SB_PLAYER_Alarm( $$@ ) { if($url !~ /:\/\//) { $url='/' . $url if ($url =~ /^[a-zA-Z]:\\/); # für Windows $url=~ s/\\/\//g; - $url='file://' . $url; + $url='file://' . $url; } # CD 0034 end $cmdstr .= " url:" . uri_escape(decode('utf-8',$url)); # CD 0015 uri_escape und join hinzugefügt # CD 0020 decode hinzugefügt # CD 0034 playlist: in url: geändert @@ -3182,7 +3507,7 @@ sub SB_PLAYER_Alarm( $$@ ) { if($url !~ /:\/\//) { $url='/' . $url if ($url =~ /^[a-zA-Z]:\\/); # für Windows $url=~ s/\\/\//g; - $url='file://' . $url; + $url='file://' . $url; } # CD 0034 end $cmdstr .= "url:" . uri_escape(decode('utf-8',$url)); # CD 0017 decode hinzugefügt # CD 0034 playlist: in url: geändert @@ -3210,7 +3535,7 @@ sub SB_PLAYER_Alarm( $$@ ) { Log3( $hash, 3, $msg ); return( $msg ); } - } + } } elsif( $arg[ 0 ] eq "wdays" ) { if( $id ne "none" ) { if( defined($arg[ 1 ]) ) { @@ -3256,7 +3581,7 @@ sub SB_PLAYER_Alarm( $$@ ) { my $msg = "SB_PLAYER_Set: please use hh:mm:ss for alarm time."; Log3( $hash, 3, $msg ); return( $msg ); - } + } my $secs = ( $buf[ 0 ] * 3600 ) + ( $buf[ 1 ] * 60 ) + $buf[ 2 ]; $cmdstr = "$hash->{PLAYERMAC} alarm update id:$id "; @@ -3264,7 +3589,7 @@ sub SB_PLAYER_Alarm( $$@ ) { IOWrite( $hash, $cmdstr ); # CD 0017 start if($updateReadingsOnSet) { - my $buf = sprintf( "%02d:%02d:%02d", + my $buf = sprintf( "%02d:%02d:%02d", int( scalar( $secs ) / 3600 ), int( ( $secs % 3600 ) / 60 ), int( $secs % 60 ) ); @@ -3280,7 +3605,7 @@ sub SB_PLAYER_Alarm( $$@ ) { # readingsSingleUpdate( $hash, "alarmid$n", "none", 1 ); # CD 0015 deaktiviert } - } else { + } else { my $msg = "SB_PLAYER_Set: unkown argument ".$arg[ 0 ]." for alarm given."; Log3( $hash, 3, $msg ); return( $msg ); @@ -3350,7 +3675,7 @@ sub SB_PLAYER_GetStatus( $ ) { if(!defined($hash->{helper}{lastGetStatus})||($hash->{helper}{lastGetStatus}{helper}{lastGetStatus}, now: ".gettimeofday(); $hash->{helper}{lastGetStatus}=gettimeofday(); - + # we fire the respective questions and parse the answers in parse IOWrite( $hash, "$hash->{PLAYERMAC} artist ?\n" ); IOWrite( $hash, "$hash->{PLAYERMAC} album ?\n" ); @@ -3376,8 +3701,8 @@ sub SB_PLAYER_GetStatus( $ ) { IOWrite( $hash, "$hash->{PLAYERMAC} duration ?\n" ); SB_PLAYER_QueryElapsedTime($hash); } # CD 0014 end - - # the other values below are provided by our server. we don't + + # the other values below are provided by our server. we don't # need to ask again if( $hash->{PLAYERIP} eq "?" ) { # the server doesn't care about us @@ -3408,9 +3733,9 @@ sub SB_PLAYER_GetStatus( $ ) { my $iv=AttrVal($name, "statusRequestInterval", 300); # CD 0037 - InternalTimer( gettimeofday() + $iv, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + $iv, + "SB_PLAYER_GetStatus", + $hash, 0 ) if $iv>0; Log3( $hash, 5, "SB_PLAYER_GetStatus: leaving" ); @@ -3427,7 +3752,7 @@ sub SB_PLAYER_RecBroadcast( $$@ ) { my $name = $hash->{NAME}; - Log3( $hash, 5, "SB_PLAYER_Broadcast($name): called with $msg" ); + Log3( $hash, 5, "SB_PLAYER_Broadcast($name): called with $msg" ); # let's see what we got. Split the data at the space my @args = split( " ", $msg ); @@ -3442,13 +3767,13 @@ sub SB_PLAYER_RecBroadcast( $$@ ) { SB_PLAYER_Amplifier( $hash ); } elsif( $args[ 0 ] eq "ON" ) { # the server is back - #readingsSingleUpdate( $hash, "state", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten - #readingsSingleUpdate( $hash, "power", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten + #readingsSingleUpdate( $hash, "state", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten + #readingsSingleUpdate( $hash, "power", "on", 1 ); # CD 0011 ob der Player eingeschaltet ist, ist hier noch nicht bekannt, SB_PLAYER_GetStatus abwarten # do and update of the status RemoveInternalTimer( $hash ); # CD 0016 - InternalTimer( gettimeofday() + 10, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + 10, + "SB_PLAYER_GetStatus", + $hash, 0 ); } elsif( $args[ 0 ] eq "IP" ) { $hash->{SBSERVER} = $args[ 1 ]; @@ -3470,22 +3795,74 @@ sub SB_PLAYER_RecBroadcast( $$@ ) { # format: ADD IODEVname ID shortentry $hash->{helper}{SB_PLAYER_Favs}{$args[3]}{ID} = $args[ 2 ]; $hash->{helper}{SB_PLAYER_Favs}{$args[3]}{URL} = $args[ 4 ]; # CD 0021 hinzugefügt + $hash->{helper}{SB_PLAYER_Favs}{$args[3]}{SOURCE} = $args[ 5 ]; # CD 0070 hinzugefügt if( $hash->{FAVSTR} eq "" ) { $hash->{FAVSTR} = $args[ 3 ]; # CD Test für Leerzeichen join(" ",@args[ 4..$#args ]); } else { $hash->{FAVSTR} .= "," . $args[ 3 ]; # CD Test für Leerzeichen join(" ",@args[ 4..$#args ]); } + + # CD 0064 start + if( AttrVal( $name, "sortFavorites", "0" ) eq "1" ) { + my @radios = split(',',$hash->{FAVSTR}); # CD0064 Elektrolurch + $hash->{FAVSTR} = join(',',sort { "\L$a" cmp "\L$b" } @radios); # CD0064 Elektrolurch + } + # CD 0064 + + # CD 0068 start + if(AttrVal($name,"ftuiSupport","") eq "1") { + my $t=$hash->{FAVSTR}; + $t=~s/,/:/g; + readingsSingleUpdate( $hash, "ftuiFavoritesItems", $t, 1 ); + $t=~s/_/ /g; + readingsSingleUpdate( $hash, "ftuiFavoritesAlias", $t, 1 ); + } + # CD 0068 end + # CD 0016 start, provisorisch um alarmPlaylists zu aktualisieren, TODO: muss von 97_SB_SERVER kommen RemoveInternalTimer( $hash ); # CD 0016 - InternalTimer( gettimeofday() + 3, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + 3, + "SB_PLAYER_GetStatus", + $hash, 0 ); #end } elsif( $args[ 0 ] eq "FLUSH" ) { - undef( %{$hash->{helper}{SB_PLAYER_Favs}} ); - $hash->{FAVSTR} = ""; - delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016 + if($args[ 1 ] eq 'all') { + undef( %{$hash->{helper}{SB_PLAYER_Favs}} ); + $hash->{FAVSTR} = ""; + delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016 + } else { + # CD 0070 gezielt nach Plugin/App löschen + my $doupdate=0; + my $favs=""; + foreach my $fa ( keys %{$hash->{helper}{SB_PLAYER_Favs}} ) { + if($hash->{helper}{SB_PLAYER_Favs}{$fa}{SOURCE} eq $args[ 1 ]) { + delete $hash->{helper}{SB_PLAYER_Favs}{$fa}; + $doupdate=1; + } else { + if( $favs eq "" ) { + $favs = $fa; + } else { + $favs .= "," . $fa; + } + } + } + if($doupdate==1) { + if( AttrVal( $name, "sortFavorites", "0" ) eq "1" ) { + my @radios = split(',',$favs); + $hash->{FAVSTR} = join(',',sort { "\L$a" cmp "\L$b" } @radios); + } else { + $hash->{FAVSTR} = $favs; + } + if(AttrVal($name,"ftuiSupport","") eq "1") { + my $t=$hash->{FAVSTR}; + $t=~s/,/:/g; + readingsSingleUpdate( $hash, "ftuiFavoritesItems", $t, 1 ); + $t=~s/_/ /g; + readingsSingleUpdate( $hash, "ftuiFavoritesAlias", $t, 1 ); + } + } + } } else { } @@ -3525,19 +3902,20 @@ sub SB_PLAYER_RecBroadcast( $$@ ) { $plname=~s/^fhem_$name//; $plname=~s/^_//; } - Log3( $hash, 4, "SB_PLAYER_RecbroadCast($name): - adding - PLAYLISTS ADD " . + Log3( $hash, 4, "SB_PLAYER_RecbroadCast($name): - adding - PLAYLISTS ADD " . "name:$args[1] id:$args[2] uid:$plname" ); $hash->{helper}{myPlaylists}{$plname}{ID} = $args[ 2 ]; $hash->{helper}{myPlaylists}{$plname}{NAME} = $args[ 1 ]; } else { # CD 0036 end - Log3( $hash, 5, "SB_PLAYER_RecbroadCast($name): - ignoring - PLAYLISTS ADD " . + Log3( $hash, 5, "SB_PLAYER_RecbroadCast($name): - ignoring - PLAYLISTS ADD " . "name:$args[1] id:$args[2] uid:$args[3]" ); } } else { # CD 0014 end - Log3( $hash, 5, "SB_PLAYER_RecbroadCast($name): PLAYLISTS ADD " . + Log3( $hash, 4, "SB_PLAYER_RecbroadCast($name): PLAYLISTS ADD " . "name:$args[1] id:$args[2] uid:$args[3]" ); + $hash->{helper}{SB_PLAYER_Playlists}{$args[3]}{SOURCE} = $args[ 4 ]; # CD 0070 $hash->{helper}{SB_PLAYER_Playlists}{$args[3]}{ID} = $args[ 2 ]; $hash->{helper}{SB_PLAYER_Playlists}{$args[3]}{NAME} = $args[ 1 ]; if( $hash->{SERVERPLAYLISTS} eq "" ) { @@ -3546,18 +3924,69 @@ sub SB_PLAYER_RecBroadcast( $$@ ) { $hash->{SERVERPLAYLISTS} .= "," . $args[ 3 ]; } } # CD 0014 + # CD 0064 start + if( AttrVal( $name, "sortPlaylists", "0" ) eq "1" ) { + my @radios = split(',',$hash->{SERVERPLAYLISTS}); + $hash->{SERVERPLAYLISTS} = join(',',sort { "\L$a" cmp "\L$b" } @radios); + } + # CD 0064 + + # CD 0068 start + if(AttrVal($name,"ftuiSupport","") eq "1") { + my $t=$hash->{SERVERPLAYLISTS}; + $t=~s/,/:/g; + readingsSingleUpdate( $hash, "ftuiPlaylistsItems", $t, 1 ); + $t=~s/_/ /g; + readingsSingleUpdate( $hash, "ftuiPlaylistsAlias", $t, 1 ); + } + # CD 0068 end + # CD 0016 start, provisorisch um alarmPlaylists zu aktualisieren, TODO: muss von 97_SB_SERVER kommen RemoveInternalTimer( $hash ); # CD 0016 - InternalTimer( gettimeofday() + 3, - "SB_PLAYER_GetStatus", - $hash, + InternalTimer( gettimeofday() + 3, + "SB_PLAYER_GetStatus", + $hash, 0 ); #end } elsif( $args[ 0 ] eq "FLUSH" ) { - undef( %{$hash->{helper}{SB_PLAYER_Playlists}} ); - undef( %{$hash->{helper}{myPlaylists}} ); # CD 0036 - $hash->{SERVERPLAYLISTS} = ""; - delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016 + if($args[ 1 ] eq 'all') { + undef( %{$hash->{helper}{SB_PLAYER_Playlists}} ); + undef( %{$hash->{helper}{myPlaylists}} ); # CD 0036 + $hash->{SERVERPLAYLISTS} = ""; + delete($hash->{helper}{alarmPlaylists}) if (defined($hash->{helper}{alarmPlaylists})); # CD 0016 + } else { + # CD 0070 gezielt nach Plugin/App löschen + my $doupdate=0; + my $pls=""; + foreach my $pl ( keys %{$hash->{helper}{SB_PLAYER_Playlists}} ) { + if($hash->{helper}{SB_PLAYER_Playlists}{$pl}{SOURCE} eq $args[ 1 ]) { + delete $hash->{helper}{SB_PLAYER_Playlists}{$pl}; + $doupdate=1; + } else { + if( $pls eq "" ) { + $pls = $pl; + } else { + $pls .= "," . $pl; + } + } + } + if($doupdate==1) { + if( AttrVal( $name, "sortPlaylists", "0" ) eq "1" ) { + my @radios = split(',',$pls); + $hash->{SERVERPLAYLISTS} = join(',',sort { "\L$a" cmp "\L$b" } @radios); + } else { + $hash->{SERVERPLAYLISTS}=$pls; + } + + if(AttrVal($name,"ftuiSupport","") eq "1") { + my $t=$hash->{SERVERPLAYLISTS}; + $t=~s/,/:/g; + readingsSingleUpdate( $hash, "ftuiPlaylistsItems", $t, 1 ); + $t=~s/_/ /g; + readingsSingleUpdate( $hash, "ftuiPlaylistsAlias", $t, 1 ); + } + } + } } else { } @@ -3590,7 +4019,7 @@ sub SB_PLAYER_ParseAlarms( $@ ) { } # CD 0016 my $lastAlarmCount=$hash->{helper}{ALARMSCOUNT}; # CD 0016 ALARMSCOUNT nach {helper} verschoben - + if( $data[ 0 ] =~ /^([0-9])*/ ) { shift( @data ); } @@ -3602,16 +4031,16 @@ sub SB_PLAYER_ParseAlarms( $@ ) { fhem( "deletereading $name alarmid.*" ); # CD 0015 alte readings entfernen my $alarmcounter=0; # CD 0015 - + foreach( @data ) { if( $_ =~ /^(id:)(\S{8})/ ) { # id is 8 non-white-space characters - # example: id:0ac7f3a2 + # example: id:0ac7f3a2 $alarmcounter+=1; # CD 0015 readingsBulkUpdate( $hash, "alarm".$alarmcounter."_id", $2 ); # CD 0015 next; } elsif( $_ =~ /^(dow:)([0-9,]*)/ ) { # C 0016 + durch * ersetzt, für dow: ohne Tage - # example: dow:1,2,4,5,6 + # example: dow:1,2,4,5,6 my $rdaystr=""; # CD 0015 if ($2 ne "") { # CD 0016 if( index( $2, "1" ) != -1 ) { @@ -3631,7 +4060,7 @@ sub SB_PLAYER_ParseAlarms( $@ ) { } if( index( $2, "6" ) != -1 ) { $rdaystr .= " Sa"; # CD 0015 - } + } if( index( $2, "0" ) != -1 ) { $rdaystr .= " Su"; # CD 0015 } @@ -3642,7 +4071,7 @@ sub SB_PLAYER_ParseAlarms( $@ ) { readingsBulkUpdate( $hash, "alarm".$alarmcounter."_wdays", $rdaystr ); # CD 0015 next; } elsif( $_ =~ /^(enabled:)([0|1])/ ) { - # example: enabled:1 + # example: enabled:1 if( $2 eq "1" ) { readingsBulkUpdate( $hash, "alarm".$alarmcounter."_state", "on" ); # CD 0015 } else { @@ -3650,7 +4079,7 @@ sub SB_PLAYER_ParseAlarms( $@ ) { } next; } elsif( $_ =~ /^(repeat:)([0|1])/ ) { - # example: repeat:1 + # example: repeat:1 if( $2 eq "1" ) { readingsBulkUpdate( $hash, "alarm".$alarmcounter."_repeat", "yes" ); # CD 0015 } else { @@ -3658,15 +4087,15 @@ sub SB_PLAYER_ParseAlarms( $@ ) { } next; } elsif( $_ =~ /^(time:)([0-9]+)/ ) { - # example: time:25200 - my $buf = sprintf( "%02d:%02d:%02d", + # example: time:25200 + my $buf = sprintf( "%02d:%02d:%02d", int( scalar( $2 ) / 3600 ), int( ( $2 % 3600 ) / 60 ), int( $2 % 60 ) ); readingsBulkUpdate( $hash, "alarm".$alarmcounter."_time", $buf ); # CD 0015 next; } elsif( $_ =~ /^(volume:)(\d{1,2})/ ) { - # example: volume:50 + # example: volume:50 readingsBulkUpdate( $hash, "alarm".$alarmcounter."_volume", $2 ); # CD 0015 next; } elsif( $_ =~ /^(url:)(\S+)/ ) { @@ -3688,7 +4117,7 @@ sub SB_PLAYER_ParseAlarms( $@ ) { } elsif( $_ =~ /^(tags:)(\S+)/ ) { next; } elsif( $_ =~ /^(fade:)([0|1])/ ) { - # example: fade:1 + # example: fade:1 if( $2 eq "1" ) { readingsBulkUpdate( $hash, "alarmsFadeIn", "on" ); # CD 0016 von MM übernommen, Namen geändert } else { @@ -3762,7 +4191,7 @@ sub SB_PLAYER_ServerTurnOn( $ ) { my $servername; Log3( $hash, 5, "SB_PLAYER_ServerTurnOn($name): please implement me" ); - + return; fhem( "set $servername on" ); @@ -3788,7 +4217,7 @@ sub SB_PLAYER_Amplifier( $ ) { my ( $hash ) = @_; my $name = $hash->{NAME}; - if( ( $hash->{AMPLIFIER} eq "none" ) || + if( ( $hash->{AMPLIFIER} eq "none" ) || ( !defined( $defs{$hash->{AMPLIFIER}} ) ) ) { # amplifier not specified delete($hash->{helper}{AMPLIFIERDELAYOFF}) if defined($hash->{helper}{AMPLIFIERDELAYOFF}); # CD 0012 @@ -3797,13 +4226,13 @@ sub SB_PLAYER_Amplifier( $ ) { my $setvalue = "off"; my $delayAmp=0.01; # CD 0043 - + Log3( $hash, 4, "SB_PLAYER_Amplifier($name): called" ); if( AttrVal( $name, "amplifier", "play" ) eq "play" ) { my $thestatus = ReadingsVal( $name, "playStatus", "pause" ); - Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode play " . + Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode play " . "and status:$thestatus" ); if( ( $thestatus eq "playing" ) || (( $thestatus eq "paused" ) && ($hash->{helper}{amplifierDelayOffPause}==0)) ) { # CD 0043 DelayOffPause abfragen @@ -3816,13 +4245,13 @@ sub SB_PLAYER_Amplifier( $ ) { } elsif( $thestatus eq "stopped" ) { $setvalue = "off"; $delayAmp=$hash->{helper}{amplifierDelayOffStop}; # CD 0043 - } else { + } else { $setvalue = "off"; } } elsif( AttrVal( $name, "amplifier", "on" ) eq "on" ) { my $thestatus = ReadingsVal( $name, "power", "off" ); - Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode on " . + Log3( $hash, 5, "SB_PLAYER_Amplifier($name): with mode on " . "and status:$thestatus" ); if( $thestatus eq "on" ) { @@ -3839,7 +4268,7 @@ sub SB_PLAYER_Amplifier( $ ) { my $actualState = ReadingsVal( "$hash->{AMPLIFIER}", "state", "off" ); - Log3( $hash, 5, "SB_PLAYER_Amplifier($name): actual:$actualState " . + Log3( $hash, 5, "SB_PLAYER_Amplifier($name): actual:$actualState " . "and set:$setvalue" ); if ( $actualState ne $setvalue) { @@ -3851,9 +4280,9 @@ sub SB_PLAYER_Amplifier( $ ) { if ((!defined($hash->{helper}{AMPLIFIERACTIVETIMER})) || ($hash->{helper}{AMPLIFIERACTIVETIMER} ne ($actualState.$setvalue))) { Log3( $hash, 5, "SB_PLAYER_Amplifier($name): delaying amplifier on/off by $delayAmp" ); RemoveInternalTimer( "DelayAmplifier:$name"); - InternalTimer( gettimeofday() + $delayAmp, + InternalTimer( gettimeofday() + $delayAmp, "SB_PLAYER_tcb_DelayAmplifier", # CD 0014 Name geändert - "DelayAmplifier:$name", + "DelayAmplifier:$name", 0 ); $hash->{helper}{AMPLIFIERACTIVETIMER}=$actualState.$setvalue; # CD 0043 } else { @@ -3863,11 +4292,11 @@ sub SB_PLAYER_Amplifier( $ ) { } # CD 0012 end fhem( "set $hash->{AMPLIFIER} $setvalue" ); - - Log3( $hash, 5, "SB_PLAYER_Amplifier($name): amplifier changed to " . + + Log3( $hash, 5, "SB_PLAYER_Amplifier($name): amplifier changed to " . $setvalue ); } else { - Log3( $hash, 5, "SB_PLAYER_Amplifier($name): no amplifier " . + Log3( $hash, 5, "SB_PLAYER_Amplifier($name): no amplifier " . "state change" ); } delete($hash->{helper}{AMPLIFIERDELAYOFF}) if (defined($hash->{helper}{AMPLIFIERDELAYOFF})); @@ -3885,8 +4314,8 @@ sub SB_PLAYER_CoverArt( $ ) { my $name = $hash->{NAME}; # return if (defined($hash->{helper}{CoverOk}) && ($hash->{helper}{CoverOk} == 1) && ( $hash->{ISREMOTESTREAM} eq "0" )); # CD 0026 added # CD 0027 removed - - # CD 0003 fix missing server + + # CD 0003 fix missing server if(!defined($hash->{SBSERVER})||($hash->{SBSERVER} eq '?')) { if ((defined($hash->{IODev})) && (defined($hash->{IODev}->{IP}))) { $hash->{SBSERVER}=$hash->{IODev}->{IP} . ":" . AttrVal( $hash->{IODev}, "httpport", "9000" ); @@ -3895,13 +4324,13 @@ sub SB_PLAYER_CoverArt( $ ) { # CD 0003 end my $lastCoverartUrl=$hash->{COVERARTURL}; # CD 0013 - + # compile the link to the album cover if(( $hash->{ISREMOTESTREAM} eq "0" ) || ($hash->{ARTWORKURL} =~ /imageproxy%2F/)) { # CD 0026 LMS 7.8/7.9 - $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" . - "current/cover_" . AttrVal( $name, "coverartheight", 50 ) . - "x" . AttrVal( $name, "coverartwidth", 50 ) . - ".jpg?player=$hash->{PLAYERMAC}&x=".int(rand(100000)); # CD 0025 added rand() to force browser refresh + $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" . + "current/cover_" . AttrVal( $name, "coverartheight", 50 ) . + "x" . AttrVal( $name, "coverartwidth", 50 ) . + ".jpg?player=$hash->{PLAYERMAC}&x=".int(rand(100000)); # CD 0025 added rand() to force browser refresh $hash->{helper}{CoverOk}=1; # CD 0026 added } elsif( $hash->{ISREMOTESTREAM} eq "1" ) { # CD 0017 Abfrage || ( $hash->{ISREMOTESTREAM} == 1 ) entfernt # CD 0011 überprüfen ob überhaupt eine URL vorhanden ist @@ -3914,19 +4343,19 @@ sub SB_PLAYER_CoverArt( $ ) { $hash->{ARTWORKURL} =~ s/$cover/$coverArtWithSize/g; $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/" . uri_unescape($hash->{ARTWORKURL}); } else { - $hash->{COVERARTURL} = "http://www.mysqueezebox.com/public/" . - "imageproxy?u=" . $hash->{ARTWORKURL} . - "&h=" . AttrVal( $name, "coverartheight", 50 ) . + $hash->{COVERARTURL} = "http://www.mysqueezebox.com/public/" . + "imageproxy?u=" . $hash->{ARTWORKURL} . + "&h=" . AttrVal( $name, "coverartheight", 50 ) . "&w=". AttrVal( $name, "coverartwidth", 50 ); } } else { $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" . - $hash->{COVERID} . "/cover_" . AttrVal( $name, "coverartheight", 50 ) . + $hash->{COVERID} . "/cover_" . AttrVal( $name, "coverartheight", 50 ) . "x" . AttrVal( $name, "coverartwidth", 50 ) . ".jpg"; } # CD 0011 Ende } else { - $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" . + $hash->{COVERARTURL} = "http://" . $hash->{SBSERVER} . "/music/" . $hash->{COVERID} . "/cover_" . AttrVal( $name, "coverartheight", 50 ) . # CD 0011 -160206228 durch $hash->{COVERID} ersetzt "x" . AttrVal( $name, "coverartwidth", 50 ) . ".jpg"; } @@ -3934,14 +4363,14 @@ sub SB_PLAYER_CoverArt( $ ) { # CD 0004, url as reading readingsBulkUpdate( $hash, "coverarturl", $hash->{COVERARTURL}); - if( ( $hash->{COVERARTLINK} eq "none" ) || - ( !defined( $defs{$hash->{COVERARTLINK}} ) ) || + if( ( $hash->{COVERARTLINK} eq "none" ) || + ( !defined( $defs{$hash->{COVERARTLINK}} ) ) || ( $hash->{COVERARTURL} eq "?" ) ) { # weblink not specified return; } else { if ($lastCoverartUrl ne $hash->{COVERARTURL}) { # CD 0013 nur bei Änderung aktualisieren - fhem( "modify " . $hash->{COVERARTLINK} . " image " . + fhem( "modify " . $hash->{COVERARTLINK} . " image " . $hash->{COVERARTURL} ); } # CD 0013 } @@ -3952,7 +4381,7 @@ sub SB_PLAYER_CoverArt( $ ) { # ---------------------------------------------------------------------------- sub SB_PLAYER_ParsePlayerStatus( $$ ) { my( $hash, $dataptr ) = @_; - + my $name = $hash->{NAME}; my $leftover = ""; my $cur = ""; @@ -4006,8 +4435,9 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { # set default values for stuff not always send my $oldsyncmaster=$hash->{SYNCMASTER}; # CD 0056 - my $oldsyncgroup=$hash->{SYNCGROUP}; # CD 0056 - + my $oldsyncgroup=""; + $oldsyncgroup=$hash->{SYNCGROUP} if defined($hash->{SYNCGROUP}); # CD 0065 + $hash->{SYNCMASTER} = "none"; $hash->{SYNCGROUP} = "none"; $hash->{SYNCMASTERPN} = "none"; # CD 0018 @@ -4045,9 +4475,9 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { push @data2,$last_d; } # CD 0003 end - + my $lastId=0; # CD 0030 - + # loop through the results foreach( @data2 ) { my $cur=$_; @@ -4081,9 +4511,9 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { # CD 0042 start if($hash->{helper}{ttsstate}==TTS_WAITFORPOWERON) { RemoveInternalTimer( "TTSStartAfterPowerOn:$name"); - InternalTimer( gettimeofday() + 0.01, + InternalTimer( gettimeofday() + 0.01, "SB_PLAYER_tcb_TTSStartAfterPowerOn", - "TTSStartAfterPowerOn:$name", + "TTSStartAfterPowerOn:$name", 0 ); } # CD 0042 end @@ -4136,7 +4566,7 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { IOWrite( $hash, "$_ status 0 500 tags:Kcu\n" ); } } - } + } } } # CD 0056 end @@ -4171,13 +4601,16 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { my @pn=split ',',"$hash->{SYNCMASTERPN},$hash->{SYNCGROUPPN}"; my @players=devspec2array("TYPE=SB_PLAYER"); $syncgroup=undef; - - foreach(@players) { - my $pn=$_; - my $phash=$defs{$_}; - if($phash->{PLAYERNAME} ~~ @pn) { - $syncgroup.="," if(defined($syncgroup)); - $syncgroup.=$pn; + + foreach my $lmsn (@pn) { # CD 0067 + foreach(@players) { + my $pn=$_; + my $phash=$defs{$_}; + if($phash->{PLAYERNAME} eq $lmsn) { # CD 0067 + $syncgroup.="," if(defined($syncgroup)); + $syncgroup.=$pn; + last; + } } } readingsBulkUpdate( $hash, "synced", $syncgroup ); @@ -4199,7 +4632,7 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { SB_PLAYER_UpdateVolumeReadings( $hash, $2, true ); # CD 0007 start if((defined($hash->{helper}{setSyncVolume}) && ($hash->{helper}{setSyncVolume} != $2))|| (!defined($hash->{helper}{setSyncVolume}))) { - SB_PLAYER_SetSyncedVolume($hash,$2); + SB_PLAYER_SetSyncedVolume($hash,$2,0); } delete $hash->{helper}{setSyncVolume}; # CD 0007 end @@ -4255,10 +4688,11 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { $hash->{helper}{elapsedTime}{VAL}=$2; $hash->{helper}{elapsedTime}{TS}=gettimeofday(); delete($hash->{helper}{saveLocked}) if (($hash->{helper}{ttsstate}==TTS_IDLE) && defined($hash->{helper}{saveLocked})); - readingsBulkUpdate( $hash, "currentTrackPosition", int($2+0.5) ); # CD 0047 + readingsBulkUpdate( $hash, "currentTrackPosition", int($2+0.5) ); # CD 0047 next; } elsif( $cur =~ /^(playlist_tracks:)(.*)/ ) { readingsBulkUpdate( $hash, "playlistTracks", $2 ); + $hash->{helper}{playlistIds}='0' if($2==0); # CD 0071 wenn playlist leer ist internen Zustand zurücksetzen next; } elsif( $cur =~ /^(playlist_cur_index:)(.*)/ ) { readingsBulkUpdate( $hash, "playlistCurrentTrack", $2+1 ); @@ -4290,7 +4724,22 @@ sub SB_PLAYER_ParsePlayerStatus( $$ ) { } } $hash->{helper}{playerStatusOK}=1; # CD 0042 - + + # CD 0065 start + if(AttrVal($name,"ftuiSupport","") eq "1") { + delete($hash->{SONGINFOQUEUE}) if(defined($hash->{SONGINFOQUEUE})); # CD 0072 + + if(defined($hash->{helper}{playlistIds})) { + $hash->{helper}{playlistInfoRetries}=5; # CD 0076 + my @ids=split(',',$hash->{helper}{playlistIds}); + foreach(@ids) { + IOWrite( $hash, $hash->{PLAYERMAC}." songinfo 0 100 track_id:".$_." tags:acdltuxNK\n" ) unless((defined($hash->{helper}{playlistInfo}{$_}) && !defined($hash->{helper}{playlistInfo}{$_}{remote})) || ($_==0)); # CD 0071, id 0 ignorieren + } + IOWrite( $hash, $hash->{PLAYERMAC}." FHEMupdatePlaylistInfoDone\n" ); + } + } + # CD 0065 end + # Matthew 0019 start if( $hash->{SYNCED} ne "yes") { readingsBulkUpdate( $hash, "synced", "none" ); @@ -4360,27 +4809,60 @@ sub SB_PLAYER_EstimateElapsedTime( $ ) { } # CD 0014 end +# CD 0065 start +# ---------------------------------------------------------------------------- +# convert volume from FHEM to LMS +# ---------------------------------------------------------------------------- +sub SB_PLAYER_FHEM2LMSVolume( $$ ) { + my( $hash, $fhemvol ) = @_; + + my $name = $hash->{NAME}; + + if($fhemvol=~/[+-]+.*/) {return $fhemvol}; # CD 0069 + + $fhemvol = int($fhemvol); + + my $offset=AttrVal($name,'volumeOffset',0); + my $lmsvol=$fhemvol+$offset; + + $lmsvol=0 if(($lmsvol<0) && ($fhemvol>=0)); + $lmsvol=100 if($lmsvol>100); + $lmsvol=-100 if($lmsvol<-100); + + return $lmsvol; +} +# CD 0065 end + # ---------------------------------------------------------------------------- # update the volume readings # ---------------------------------------------------------------------------- sub SB_PLAYER_UpdateVolumeReadings( $$$ ) { my( $hash, $vol, $bulk ) = @_; - + my $name = $hash->{NAME}; $vol = int($vol); # MM 0016, Fix wegen AirPlay-Plugin - if( $bulk == true ) { - readingsBulkUpdate( $hash, "volumeStraight", $vol ); + # CD 0065 start + $hash->{helper}{lmsvolume}=$vol; + my $offset=AttrVal($name,'volumeOffset',0); + my $fhemvol=$vol-$offset; + $fhemvol=0 if(($fhemvol<0) && ($vol>=0)); + $fhemvol=100 if($fhemvol>100); + $fhemvol=-100 if($fhemvol<-100); + # CD 0065 end + + if( $bulk == true ) { + readingsBulkUpdate( $hash, "volumeStraight", $fhemvol ); # CD 0065 $fhemvol verwenden if( $vol > 0 ) { - readingsBulkUpdate( $hash, "volume", $vol ); + readingsBulkUpdate( $hash, "volume", $fhemvol ); # CD 0065 $fhemvol verwenden } else { readingsBulkUpdate( $hash, "volume", "muted" ); } } else { - readingsSingleUpdate( $hash, "volumeStraight", $vol, 0 ); + readingsSingleUpdate( $hash, "volumeStraight", $fhemvol, 0 ); # CD 0065 $fhemvol verwenden if( $vol > 0 ) { - readingsSingleUpdate( $hash, "volume", $vol, 0 ); + readingsSingleUpdate( $hash, "volume", $fhemvol, 0 ); # CD 0065 $fhemvol verwenden } else { readingsSingleUpdate( $hash, "volume", "muted", 0 ); } @@ -4392,24 +4874,35 @@ sub SB_PLAYER_UpdateVolumeReadings( $$$ ) { # ---------------------------------------------------------------------------- # set volume of synced players # ---------------------------------------------------------------------------- -sub SB_PLAYER_SetSyncedVolume( $$ ) { - my( $hash, $vol ) = @_; - +sub SB_PLAYER_SetSyncedVolume( $$$ ) { + my( $hash, $vol, $isFHEMvol ) = @_; + my $name = $hash->{NAME}; return if (!defined($hash->{SYNCED}) || ($hash->{SYNCED} ne "yes")); my $sva=AttrVal($name, "syncVolume", undef); my $t=$hash->{SYNCGROUP}.",".$hash->{SYNCMASTER}; - + $vol = int($vol); # MM 0016, Fix wegen AirPlay-Plugin $hash->{helper}{setSyncVolume}=$vol; - + if(defined($sva) && ($sva ne "0") && ($sva ne "1")) { my @pl=split(",",$t); my @chlds=devspec2array("TYPE=SB_PLAYER"); + # CD 0065 start + if($isFHEMvol==0) { + my $fhemvol=$vol-AttrVal($name,'volumeOffset',0); + $fhemvol=0 if(($fhemvol<0) && ($vol>=0)); + $fhemvol=100 if($fhemvol>100); + $fhemvol=-100 if($fhemvol<-100); + #Log 0,$name.": convert volume from ".$vol." to ".$fhemvol; + $vol=$fhemvol; + } + # CD 0065 end + foreach (@pl) { if (($_ ne "?") && ($_ ne $hash->{PLAYERMAC})) { my $mac=$_; @@ -4419,7 +4912,7 @@ sub SB_PLAYER_SetSyncedVolume( $$ ) { my $sva2=AttrVal($chash->{NAME}, "syncVolume", undef); if (defined($sva2) && ($sva eq $sva2)) { if ($vol>0) { # CD 0010 - if(ReadingsVal($chash->{NAME}, "volumeStraight", $vol)!=$vol) { + if(ReadingsVal($chash->{NAME}, "volumeStraight", $vol) ne $vol) { #Log 0,$chash->{NAME}." setting volume to ".$vol." (from ".$hash->{NAME}.")"; $chash->{helper}{setSyncVolume}=$vol; fhem "set ".$chash->{NAME}." volumeStraight ".$vol." x"; @@ -4453,7 +4946,7 @@ sub SB_PLAYER_PlayerStatefileName($) my $statefile = $attr{global}{statefile}; $statefile = substr $statefile,0,rindex($statefile,'/')+1; return $statefile ."sbps_$name.dd.save"; -} +} sub SB_PLAYER_SavePlayerStates($) { @@ -4482,7 +4975,7 @@ sub SB_PLAYER_SavePlayerStates($) } return undef; -} +} sub SB_PLAYER_LoadPlayerStates($) { @@ -4510,7 +5003,66 @@ sub SB_PLAYER_LoadPlayerStates($) Log3 undef, 4, $msg; } return undef; -} +} + +# CD 0072 start +sub +SB_PLAYER_SonginfoAddQueue($$$) ################################################## +{ + my ($hash, $id, $wait) = @_; + my $name = $hash->{NAME}; + + if(!$hash->{SONGINFOQUEUE}) { + $hash->{SONGINFOQUEUE} = [ $id ]; + push(@{$hash->{SONGINFOQUEUE}}, $id); + RemoveInternalTimer( "SonginfoHandleQueue:$name"); + InternalTimer( gettimeofday() + ($wait==1?2.0:0.01), + "SB_PLAYER_tcb_SonginfoHandleQueue", + "SonginfoHandleQueue:$name", + 0 ); + } else { + push(@{$hash->{SONGINFOQUEUE}}, "wait") if ($wait==1); + push(@{$hash->{SONGINFOQUEUE}}, $id); + } +} + +sub +SB_PLAYER_SonginfoHandleQueue($) ################################################## +{ + my $hash = shift; + my $name = $hash->{NAME}; + + my $arr = $hash->{SONGINFOQUEUE}; + if(defined($arr) && @{$arr} > 0) { + shift(@{$arr}); + if(@{$arr} == 0) { + IOWrite( $hash, $hash->{PLAYERMAC}." FHEMupdatePlaylistInfoDone\n" ); + delete($hash->{SONGINFOQUEUE}); + return; + } + my $id = $arr->[0]; + if($id ne "wait") { + if(($id eq "") || ($id==0)) { # CD 0076 id 0 ignorieren + SB_PLAYER_SonginfoHandleQueue($hash); + } else { + IOWrite( $hash, $hash->{PLAYERMAC}." songinfo 0 100 track_id:".$id." tags:acdltuxNK\n" ); + } + } + InternalTimer( gettimeofday() + (($id eq "wait")?2.0:0.01), + "SB_PLAYER_tcb_SonginfoHandleQueue", + "SonginfoHandleQueue:$name", + 0 ); + } +} + +sub SB_PLAYER_tcb_SonginfoHandleQueue($) { + my($in ) = shift; + my(undef,$name) = split(':',$in); + my $hash = $defs{$name}; + + SB_PLAYER_SonginfoHandleQueue($hash); +} +# CD 0072 end # ############################################################################## # No PERL code beyond this line @@ -4518,11 +5070,11 @@ sub SB_PLAYER_LoadPlayerStates($) 1; =pod -=item device +=item device =item summary control Squeezebox Media Players -=item summary_DE Ansteuerung von Squeezebox Media Playern +=item summary_DE Ansteuerung von Squeezebox Media Playern =begin html - +

SB_PLAYER

    @@ -4545,13 +5097,13 @@ sub SB_PLAYER_LoadPlayerStates($) This may be used to show coverart in a floorplan.


- + Set
    set <name> <command> [<parameter>]

    - + SB_Player related commands:

    • play - start playback (might not work unless previously paused).
    • @@ -4560,18 +5112,19 @@ sub SB_PLAYER_LoadPlayerStates($)
    • next|channelUp - next track
    • prev|channelDown - previous track or the beginning of the current track.
    • mute - toggle mute.
    • -
    • volume <n> - set volume to <n>. <n> must be a number between 0 and 100
    • +
    • volume <n> - set volume to <n>. <n> must be a number between 0 and 100.
    • +
    • volume +|-<n> - increase|decrease volume by a value given with +|-<n>. <n> must be a number between 0 and 100.
    • volumeStraight <n> - same as volume
    • -
    • volumeDown - reduce the volume by a number of steps given with the attribute volumeStep; default 10 steps
    • -
    • volumeUp - increase the volume by a number of steps given with the attribute volumeStep; default 10 steps
    • +
    • volumeDown - reduce the volume by a number of steps given with the attribute volumeStep; default 10 steps.
    • +
    • volumeUp - increase the volume by a number of steps given with the attribute volumeStep; default 10 steps.
    • on - turn player on if possible. Issue play command otherwise.
    • off - turn player off if possible. Issue stop command otherwise.
    • shuffle off|song|album - enable/disable shuffle mode.
    • repeat one|all|off - Set repeat mode.
    • sleep <timespec> - Set player off after <timespec> has expired, fading player volume down. <timespec>'s format is hh:mm:ss. - <timespec> is a relative time specification. That means: set <myPlayer> sleep 22:00:00 doesn’t switch the player off at 10pm but after 22 hours
    • + <timespec> is a relative time specification. That means: set <myPlayer> sleep 22:00:00 doesn’t switch the player off at 10pm but after 22 hours
    • favorites <favorite> - Empty current playlist and start <favorite>. - <favorite> is a selected favorite from out of the favorite-list. The frontend may make favorites selectable by a dropdown list.
    • + <favorite> is a selected favorite from out of the favorite-list. The frontend may make favorites selectable by a dropdown list.
    • talk|sayText <text> - Save the playlist, speak <text> using Google TTS and resume saved playlist.
    • playlist track|album|artist|genre|year <x> - Empty current playlist and play given argument.
    • playlist genre:<genre> artist:<artist> album:<album> - Empty current playlist and play all tracks matching the parameters. A * acts as a wildcard for everything.
    • @@ -4581,6 +5134,8 @@ sub SB_PLAYER_LoadPlayerStates($)
    • playlist add <filename|playlistname> - Add the specified file or playlist at the end of the current playlist.
    • playlist insert <filename|playlistname> - Insert specified file or playlist after the current track into the current playlist.
    • +
    • track|tracknumber <n|+n|-n> - Sets the track with the given tracknumber as the current title. An explicitly + positive or negative number may be used to jump to a song relative to the currently playing song..
    • statusRequest - Update all readings.
    • sync <playerName[,playerName...]> [new|asSlave] - Put playerName(s) into this player's multiroom group. Remove playerName(s) from their existing group(s) if necessary. Options:
      • @@ -4613,8 +5168,10 @@ sub SB_PLAYER_LoadPlayerStates($)
      • line2 - Text to be displayed in the second line
      • duration - Duration of display in seconds
      -
    • CurrentTrackPosition <TimePosition> - Sets the current timeposition inside the title to the given value.
    • -
    • resetTTS - Resets TTS, can be necessary if the output via Google blocks.
    • +
    • currentTrackPosition <TimePosition> - Sets the current timeposition inside the title to the given value.
    • +
    • resetTTS - Resets TTS, can be necessary if the output via TTS blocks.
    • +
    • updateFTUImedialist - Manually refresh the reading ftuiMedialist (containing the current songs).
    • +
    • clearFTUIcache - Clear the cached data used for the FTUI-readings.

    Alarms
    @@ -4678,14 +5235,20 @@ sub SB_PLAYER_LoadPlayerStates($) Only changes in the readings currentAlbum, currentArtist, currentTitle cause an event.
  • fadeinsecs <sec1>[,<sec2>]
    Fade-in period in seconds. A second comma separated value optionally specifies the period to use on unpause.
  • -
  • idismac true|false
    - Determines if the MAC-adress should be the unique identifier
  • +
  • ftuiSupport 0|1
    + If set to 1 additional readings for FTUI integration are created.
  • +
  • sortFavorites 0|1
    + If set to 1 the favorites will be sorted alphabetically.
  • +
  • sortPlaylists 0|1
    + If set to 1 the playlists will be sorted alphabetically.
  • statusRequestInterval <sec>
    Interval in seconds for automatic status requests. Default: 300
  • syncedNamesSource FHEM|LMS
    Determines if the synced reading contains the LMS or FHEM names of the players
  • syncVolume 0|1
    Defines whether the loudness of this player is synchronized with the other players of its group
  • +
  • trackPositionQueryInterval <sec>
    + Interval in seconds for querying the current track position, 0 disables the periodic update of the track position
  • ttsAPIKey <API-key>
    For the use of T2Speech from the company VoiceRSS (voicerss.org) an API-key is needed. Not needed with Google’s T2Speech.
  • ttsDelay <sec1>[,<sec2>]
    @@ -4695,7 +5258,7 @@ sub SB_PLAYER_LoadPlayerStates($)
  • ttslink <link>
    Enter the link to the TTS-Funktion. Google, VoiceRSS and –with reservations- Text2Speech are supported.

    Example of using Google’s TTS:
    - attr <playername> ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob

  • + attr <playername> ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob
  • ttsMP3FileDir <directory>
    Directory to be used by default for text-embedded MP3-Files.

  • Example:
    @@ -4706,8 +5269,8 @@ sub SB_PLAYER_LoadPlayerStates($)
  • debugsaverestore - Additional information about loading/saving of player‘s status are written into the logfile
  • nosaverestore - Do not save and restore the status of the player, thereby the normal playing stops after using TTS.
  • forcegroupon - Switch on all players of the group.
  • -
  • ignorevolumelimit - Ignore the attribute volumeLimit while using TTS-output
  • -
  • doubleescape - Alternative encoding for letters with accents
  • +
  • ignorevolumelimit - Ignore the attribute volumeLimit while using TTS-output.
  • +
  • eventondone - Fire an event at the end of the TTS output.
  • ttsPrefix <text>
    Text prepended to every text to speech output
  • @@ -4718,6 +5281,8 @@ sub SB_PLAYER_LoadPlayerStates($) If set to true, most readings are immediately updated when a set command is executed without waiting for a reply from the server.
  • volumeLimit <value>
    Upper limit for volume setting by FHEM.
  • +
  • volumeOffset <value>
    + The offset is added to the volume sent to the server. It is subtracted from the volume returned by the server.
  • volumeStep <value>
    Sets the volume adjustment to a granularity given by <value>. Possible values: 1–100. Default value: 10
  • @@ -4725,7 +5290,7 @@ sub SB_PLAYER_LoadPlayerStates($) =end html =begin html_DE - +

    SB_PLAYER

      @@ -4744,20 +5309,20 @@ sub SB_PLAYER_LoadPlayerStates($) Optional

      • <[ampl]>: Ein FHEM-Device welches ein- oder ausgeschaltet werden soll, wenn - ein passendes Event empfangen wird. Das Attribut + ein passendes Event empfangen wird. Das Attribut amplifier gibt an, ob das passende Event on/off oder play/pause sein soll.
      • <[coverart]>: Unter <coverart> gibt man den Namen eines FHEM weblink image Elementes an. Der Player aktualisiert dann jeweils den Link auf das aktuelle Coverart Bild, so dass man dieses z.B. im Floorplan anzeigen kann.


    - + Set
      set <Name> <Befehl> [<Parameter>]

      - + Befehle zur Steuerung des Players:

      • play - Startet die Wiedergabe (Es kann sein, dass die Wiedergabe nicht funktioniert, @@ -4770,6 +5335,8 @@ sub SB_PLAYER_LoadPlayerStates($)
      • mute - Toggelt die Lautstärke stumm und wieder zurück.
      • volume <n> - Stellt die Lautstärke auf einen Wert <n> ein. Dabei muss <n> eine Zahl zwischen 0 und 100 sein.
      • +
      • volume +|-<n> - Erhöht oder vermindert die Lautstärke um den Wert, der durch +|-<n> + vorgegeben wird. Dabei muss <n> eine Zahl zwischen 0 und 100 sein.
      • volumeStraight <n> - mit volume identisch
      • volumeDown - Verringert die Lautstärke um eine bestimmte Anzahl von Schritten die mit dem Attribut volumeStep vorgegeben werden. Wird nichts vorgegeben, werden 10 Schritte verwendet
      • @@ -4785,16 +5352,16 @@ sub SB_PLAYER_LoadPlayerStates($) Format hh:mm[:ss]. Bei der Zeitangabe handelt es sich um eine relative Zeitangabe ab dem Aufruf. Das bedeutet: set <myPlayer> sleep 22:00:00 schaltet den Player nicht um 10 Uhr abends aus, - sondern erst 22 Stunden nach dem Aufruf! + sondern erst 22 Stunden nach dem Aufruf!
      • favorites <favorite> - Löscht die aktuelle Wiedergabeliste und startet den Favoriten <favorite>. <favorite> ist der ausgewählte Favorit aus einer Favoritenliste. Das Frontend kann eine - Dropdownliste zur Verfügung stellen, aus der der Favorit ausgewählt werden kann.
      • + Dropdownliste zur Verfügung stellen, aus der der Favorit ausgewählt werden kann.
      • talk|sayText <text> - Speichert den aktuellen Stand der Playlist, sagt den Text <text> unter Zuhilfenahme von GoogleTTS oder VoiceRSS an und setzt die Wiedergabe ab dem gespeicherten Punkt fort.
      • playlist track|album|artist|genre|year <x> - Löscht die aktuelle Wiedergabeliste und gibt das vorgegebene Argument <x> wieder.
      • -
      • playlist genre:<genre> artist:<artist> album:<album> - +
      • playlist genre:<genre> artist:<artist> album:<album> - Löscht die aktuelle Wiedergabeliste und gibt alle Tracks wieder, die den angegebenen Kriterien entsprechen. Ein * steht für beliebige Angaben.

      • Beispiel:
        @@ -4805,6 +5372,9 @@ sub SB_PLAYER_LoadPlayerStates($) an das Ende der aktuellen Playlist an.
      • playlist insert <filename|playlistname> - Fügt die angegebene Datei oder Playlist hinter der aktuellen Datei oder Playlist in der aktuellen Playlist ein.
      • +
      • track|tracknumber <n|+n|-n> - Aktiviert einen bestimmten Titel der aktiven Playliste. Mit <n> ist + die laufende Nummer des Titels innerhalb dieser Playliste anzugeben. Ein explizierter Wert mit Vorzeichen, + springt in der momentanen Playliste um <+n> oder <-n> Titel vor oder zurück.
      • statusRequest - Aktualisierung aller Readings.
      • sync <playerName[,playerName...]> [new|asSlave] - Fügt den/die Player mit dem/den Namen <playerName> der Multiroom-Gruppe desjenigen Players hinzu, der diesen Befehl aufgerufen hat; @@ -4846,9 +5416,11 @@ sub SB_PLAYER_LoadPlayerStates($)
      • text2 - Inhalt der zweiten Zeile
      • duration - Dauer der Anzeige auf dem Display in Sekunden
      -
    • CurrentTrackPosition <TimePosition> - Setzt die Abspielposition innerhalb des +
    • currentTrackPosition <TimePosition> - Setzt die Abspielposition innerhalb des Liedes auf den angegebenen Zeitwert (z.B. 0:01:15).
    • -
    • resetTTS - TTS zurücksetzen, kann nötig sein wenn die Ausgabe über Google hängt.
    • +
    • resetTTS - TTS zurücksetzen, kann nötig sein wenn die TTS-Ausgabe hängt.
    • +
    • updateFTUImedialist - Erzwingt die Aktualisierung des Readings ftuiMedialist (enthält die aktuellen Songs).
    • +
    • clearFTUIcache - Löscht alle zwischengespeicherten Daten für die Erstellung der FTUI-Readings.

    Befehlsliste zur Steuerung der Wecker, mehrere Wecker können definiert werden.

    @@ -4931,14 +5503,20 @@ sub SB_PLAYER_LoadPlayerStates($) Fade in für Beginn von Playlisten und neuen Soundfiles. Bezeichnet die Dauer des Vorganges, in der die Lautstärke auf den vorgegebenen Wert ansteigt und wird in Sekunden angegeben. Ein zweiter, durch Komma getrennter optionaler Wert, gibt die Dauer des Fadein beim Verlassen des Pausenzustandes an. -
  • idismac true|false
    - Soll die MAC-Adresse des Players als ID genommen werden, muss true (default) eingetragen werden, sonst false.
  • +
  • ftuiSupport 0|1
    + Wenn das Attribut den Wert 1 hat werden zusätzliche Readings für die Integration in FTUI erzeugt.
  • statusRequestInterval <sec>
    Aktualisierungsintervall der automatischen Status-Abfrage. Default: 300
  • +
  • sortFavorites 0|1
    + Wenn das Attribut den Wert 1 hat wird die Liste der Favoriten alphabetisch sortiert.
  • +
  • sortPlaylists 0|1
    + Wenn das Attribut den Wert 1 hat wird die Liste der Wiedergabelisten alphabetisch sortiert.
  • syncedNamesSource FHEM|LMS
    Legt fest ob die FHEM-Gerätenamen oder die LMS-Namen der Player im synced-Reading verwendet werden.
  • syncVolume 0|1
    Legt fest ob die Lautstärke des Players mit anderen Playern aus der Gruppe synchronisiert wird.
  • +
  • trackPositionQueryInterval <sec>
    + Legt fest wie häufig die abgespielte Zeit abgefragt wird, 0 deaktiviert das Abfragen.
  • ttsAPIKey <API-key>
    Für die Benutzung von T2Speech der Firma VoiceRSS (voicerss.org) ist ein API-Key erforderlich. Für Googles T2Speech ist kein Key notwendig.
  • @@ -4952,7 +5530,7 @@ sub SB_PLAYER_LoadPlayerStates($)
  • ttslink <link>
    Link zur TTS-Funktion. Unterstützt werden: Google, VoiceRSS (API-Key wird benötigt) und mit Einschränkungen Text2Speech.

    Beispiel für Google’s TTS:
    - attr <playername> ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob

  • + attr <playername> ttslink http://translate.google.com/translate_tts?ie=UTF-8&tl=<LANG>&q=<TEXT>&client=tw-ob
  • ttsMP3FileDir <directory>
    Standard-Verzeichnis für Musik- und Sprachdateien (z.B. Gong, Sirene, Ansagen,...) im mp3-Format, die beim TTS eingebunden werden sollen.

  • @@ -4964,8 +5542,8 @@ sub SB_PLAYER_LoadPlayerStates($)
  • debugsaverestore - Schreibt zusätzliche Meldungen zum Speichern/Laden des Playerzustandes ins Log.
  • nosaverestore - Zustand des Players nicht sichern und wiederherstellen, dadurch stoppt die Wiedergabe nach Abspielen des TTS.
  • forcegroupon - Player in der Gruppe werden eingeschaltet.
  • -
  • ignorevolumelimit - Attribut volumeLimit für die TTS-Ausgabe ignorieren
  • -
  • doubleescape - alternative Codierung der Sonderzeichen
  • +
  • ignorevolumelimit - Attribut volumeLimit für die TTS-Ausgabe ignorieren.
  • +
  • eventondone - Erzeugt ein Event wenn die Sprachausgabe zu Ende ist.
  • ttsPrefix <text>
    Text, der vor jede TTS-Ausgabe gehangen wird
  • @@ -4979,6 +5557,9 @@ sub SB_PLAYER_LoadPlayerStates($) soweit wie möglich sofort aktualisiert.
  • volumeLimit <value>
    Oberer Grenzwert zur Lautstärkeeinstellung durch FHEM.
  • +
  • volumeOffset <value>
    + Offset der zur Lautstärke die zum LMS geschickt wird addiert wird. Für die Anzeige der aktuellen Lautstärke wird der + Wert von der vom LMS geschickten Lautstärke abgezogen.
  • volumeStep <value>
    Hier wird die Granularität der Lautstärkeregelung festgelegt. Default ist 10