diff --git a/21_HEOSGroup.pm b/21_HEOSGroup.pm index b967bc5..9cc53d4 100644 --- a/21_HEOSGroup.pm +++ b/21_HEOSGroup.pm @@ -37,207 +37,212 @@ use warnings; use JSON qw(decode_json); use Encode qw(encode_utf8); - my $version = "1.0.3"; +sub HEOSGroup_Initialize { + my $hash = shift; + $hash->{Match} = '.*{"command":."group.*|.*{"command":."event\/group.*'; - -# Declare functions -sub HEOSGroup_Initialize($); -sub HEOSGroup_Define($$); -sub HEOSGroup_Undef($$); -sub HEOSGroup_Attr(@); -sub HEOSGroup_Notify($$); -sub HEOSGroup_Parse($$); -sub HEOSGroup_WriteReadings($$); -sub HEOSGroup_Set($$@); -sub HEOSGroup_PreProcessingReadings($$); -sub HEOSGroup_GetGroupInfo($); -sub HEOSGroup_GetGroupVolume($); -sub HEOSGroup_GetGroupMute($); - - - - -sub HEOSGroup_Initialize($) { - - my ($hash) = @_; - - $hash->{Match} = '.*{"command":."group.*|.*{"command":."event\/group.*'; - - # Provider - $hash->{SetFn} = "HEOSGroup_Set"; - $hash->{DefFn} = "HEOSGroup_Define"; - $hash->{UndefFn} = "HEOSGroup_Undef"; - $hash->{NotifyFn} = "HEOSGroup_Notify"; - $hash->{AttrFn} = "HEOSGroup_Attr"; - $hash->{ParseFn} = "HEOSGroup_Parse"; - $hash->{AttrList} = "IODev ". - "disable:1 ". - $readingFnAttributes; + $hash->{SetFn} = "HEOSGroup_Set"; + $hash->{DefFn} = "HEOSGroup_Define"; + $hash->{UndefFn} = "HEOSGroup_Undef"; + $hash->{NotifyFn} = "HEOSGroup_Notify"; + $hash->{AttrFn} = "HEOSGroup_Attr"; + $hash->{ParseFn} = "HEOSGroup_Parse"; + $hash->{AttrList} = "IODev " . "disable:1 " . $readingFnAttributes; + + foreach my $d ( sort keys %{ $modules{HEOSGroup}{defptr} } ) { - foreach my $d(sort keys %{$modules{HEOSGroup}{defptr}}) { - my $hash = $modules{HEOSGroup}{defptr}{$d}; - $hash->{VERSION} = $version; + $hash->{VERSION} = $version; } } -sub HEOSGroup_Define($$) { - +sub HEOSGroup_Define { my ( $hash, $def ) = @_; + my @a = split( "[ \t]+", $def ); splice( @a, 1, 1 ); + my $iodev; my $i = 0; - - foreach my $param ( @a ) { - if( $param =~ m/IODev=([^\s]*)/ ) { - + foreach my $param (@a) { + if ( $param =~ m/IODev=([^\s]*)/ ) { + $iodev = $1; splice( @a, $i, 3 ); last; } - + $i++; } - - return "too few parameters: define HEOSGroup " if( @a < 2 ); - my ($name,$gid) = @a; + return "too few parameters: define HEOSGroup " if ( @a < 2 ); + + my ( $name, $gid ) = @a; + + $hash->{GID} = $gid; + $hash->{VERSION} = $version; + $hash->{NOTIFYDEV} = "HEOSPlayer" . abs($gid); + AssignIoPort( $hash, $iodev ) if ( !$hash->{IODev} ); + + if ( defined( $hash->{IODev}->{NAME} ) ) { + + Log3 $name, 3, + "HEOSGroup ($name) - I/O device is " . $hash->{IODev}->{NAME}; + + } + else { - $hash->{GID} = $gid; - $hash->{VERSION} = $version; - $hash->{NOTIFYDEV} = "HEOSPlayer".abs($gid); - AssignIoPort($hash,$iodev) if( !$hash->{IODev} ); - - if(defined($hash->{IODev}->{NAME})) { - - Log3 $name, 3, "HEOSGroup ($name) - I/O device is " . $hash->{IODev}->{NAME}; - - } else { - Log3 $name, 1, "HEOSGroup ($name) - no I/O device"; } - + $iodev = $hash->{IODev}->{NAME}; my $code = abs($gid); - $code = $iodev."-".$code if( defined($iodev) ); + $code = $iodev . "-" . $code if ( defined($iodev) ); my $d = $modules{HEOSGroup}{defptr}{$code}; - - return "HEOSGroup device $hash->{GID} on HEOSMaster $iodev already defined as $d->{NAME}." - if( defined($d) && $d->{IODev} == $hash->{IODev} && $d->{NAME} ne $name ); + + return +"HEOSGroup device $hash->{GID} on HEOSMaster $iodev already defined as $d->{NAME}." + if ( defined($d) + && $d->{IODev} == $hash->{IODev} + && $d->{NAME} ne $name ); Log3 $name, 3, "HEOSGroup ($name) - defined with Code: $code"; - - $attr{$name}{room} = "HEOS" if( !defined( $attr{$name}{room} ) ); - $attr{$name}{devStateIcon} = "on:10px-kreis-gruen off:10px-kreis-rot" if( !defined( $attr{$name}{devStateIcon} ) ); - - if( $init_done ) { - - InternalTimer( gettimeofday()+int(rand(2)), "HEOSGroup_GetGroupInfo", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(4)), "HEOSGroup_GetGroupVolume", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(6)), "HEOSGroup_GetGroupMute", $hash, 0 ); - - } else { - - InternalTimer( gettimeofday()+15+int(rand(2)), "HEOSGroup_GetGroupInfo", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(4)), "HEOSGroup_GetGroupVolume", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(6)), "HEOSGroup_GetGroupMute", $hash, 0 ); + + $attr{$name}{room} = "HEOS" if ( !defined( $attr{$name}{room} ) ); + $attr{$name}{devStateIcon} = "on:10px-kreis-gruen off:10px-kreis-rot" + if ( !defined( $attr{$name}{devStateIcon} ) ); + + if ($init_done) { + + InternalTimer( gettimeofday() + int( rand(2) ), + "HEOSGroup_GetGroupInfo", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(4) ), + "HEOSGroup_GetGroupVolume", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(6) ), + "HEOSGroup_GetGroupMute", $hash, 0 ); + } - + else { + + InternalTimer( gettimeofday() + 15 + int( rand(2) ), + "HEOSGroup_GetGroupInfo", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(4) ), + "HEOSGroup_GetGroupVolume", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(6) ), + "HEOSGroup_GetGroupMute", $hash, 0 ); + } + readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'state','Initialized'); - readingsBulkUpdate($hash, 'volumeUp', 5); - readingsBulkUpdate($hash, 'volumeDown', 5); - readingsEndUpdate($hash, 1); - + readingsBulkUpdate( $hash, 'state', 'Initialized' ); + readingsBulkUpdate( $hash, 'volumeUp', 5 ); + readingsBulkUpdate( $hash, 'volumeDown', 5 ); + readingsEndUpdate( $hash, 1 ); + $modules{HEOSGroup}{defptr}{$code} = $hash; - return undef; + + return; } -sub HEOSGroup_Undef($$) { - +sub HEOSGroup_Undef { my ( $hash, $arg ) = @_; + my $name = $hash->{NAME}; - RemoveInternalTimer($hash); - my $code = abs($hash->{GID}); - $code = $hash->{IODev}->{NAME} ."-". $code if( defined($hash->{IODev}->{NAME}) ); - delete($modules{HEOSGroup}{defptr}{$code}); - + my $code = abs( $hash->{GID} ); + $code = $hash->{IODev}->{NAME} . "-" . $code + if ( defined( $hash->{IODev}->{NAME} ) ); + delete( $modules{HEOSGroup}{defptr}{$code} ); + Log3 $name, 3, "HEOSGroup ($name) - device $name deleted with Code: $code"; - return undef; + return; } -sub HEOSGroup_Attr(@) { - +sub HEOSGroup_Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; - my $hash = $defs{$name}; + + my $hash = $defs{$name}; my $token = $hash->{IODev}->{TOKEN}; - - if( $attrName eq "disable" ) { - if( $cmd eq "set" and $attrVal eq "1" ) { - - readingsSingleUpdate ( $hash, "state", "disabled", 1 ); + if ( $attrName eq "disable" ) { + if ( $cmd eq "set" and $attrVal eq "1" ) { + + readingsSingleUpdate( $hash, "state", "disabled", 1 ); Log3 $name, 3, "HEOSGroup ($name) - disabled"; - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSGroup ($name) - enabled"; } } - - if( $attrName eq "disabledForIntervals" ) { - if( $cmd eq "set" ) { - + + if ( $attrName eq "disabledForIntervals" ) { + if ( $cmd eq "set" ) { + Log3 $name, 3, "HEOSGroup ($name) - enable disabledForIntervals"; - readingsSingleUpdate ( $hash, "state", "Unknown", 1 ); - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + readingsSingleUpdate( $hash, "state", "Unknown", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSGroup ($name) - delete disabledForIntervals"; } } + + return; } -sub HEOSGroup_Notify($$) { +sub HEOSGroup_Notify { + my ( $hash, $dev ) = @_; - my ($hash,$dev) = @_; my $name = $hash->{NAME}; + return undef if ( IsDisabled($name) ); - return undef if(IsDisabled($name)); + my $events = deviceEvents( $dev, 1 ); - my $events = deviceEvents($dev,1); - - return if( !$events ); + return if ( !$events ); readingsBeginUpdate($hash); - my %playerEevents = map { my ( $key, $value ) = split /:\s/; ( $key, $value ) } @$events; + my %playerEevents = + map { my ( $key, $value ) = split /:\s/; ( $key, $value ) } @$events; foreach my $key ( keys %playerEevents ) { - + #### playing Infos - readingsBulkUpdate( $hash, $key, $playerEevents{$key} ) if( grep { $_ =~ /$key/ } ("channel", "currentAlbum", "currentArtist", "currentImageUrl", "currentMedia", "currentMid", "currentQid", "currentSid", "currentStation", "currentTitle", "error", "playStatus", "repeat", "shuffle" ) ); + readingsBulkUpdate( $hash, $key, $playerEevents{$key} ) + if ( + grep { $_ =~ /$key/ } ( + "channel", "currentAlbum", + "currentArtist", "currentImageUrl", + "currentMedia", "currentMid", + "currentQid", "currentSid", + "currentStation", "currentTitle", + "error", "playStatus", + "repeat", "shuffle" + ) + ); } - + readingsEndUpdate( $hash, 1 ); + + return; } -sub HEOSGroup_Set($$@) { - - my ($hash, $name, @aa) = @_; - my ($cmd, @args) = @aa; - my $gid = $hash->{GID}; +sub HEOSGroup_Set { + my ( $hash, $name, @aa ) = @_; + my ( $cmd, @args ) = @aa; + + my $gid = $hash->{GID}; my $action; my $heosCmd; my $rvalue; @@ -245,391 +250,465 @@ sub HEOSGroup_Set($$@) { my $favoritcount = 1; my $string = "gid=$gid"; - #senden von Befehlen unterdrücken solange state nicht on ist - return undef unless ( ReadingsVal($name, "state", "off") eq "on" ); - - if( $cmd eq 'getGroupInfo' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - - } elsif( $cmd eq 'mute' ) { + return undef unless ( ReadingsVal( $name, "state", "off" ) eq "on" ); + + if ( $cmd eq 'getGroupInfo' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + + } + elsif ( $cmd eq 'mute' ) { my $param = "on|off"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - $heosCmd = 'setGroupMute'; - $action = "state=$args[0]"; - - } elsif( $cmd eq 'volume' ) { - return "usage: $cmd 0-100" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 100 || $args[0] < 0 ); - - $heosCmd = 'setGroupVolume'; - $action = "level=$args[0]"; - - } elsif( $cmd eq 'volumeUp' ) { - return "usage: $cmd 0-10" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 10 || $args[0] < 1 ); - - $heosCmd = 'GroupVolumeUp'; - $action = "step=$args[0]"; - - } elsif( $cmd eq 'volumeDown' ) { - return "usage: $cmd 0-10" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 10 || $args[0] < 1 ); - - $heosCmd = 'groupVolumeDown'; - $action = "step=$args[0]"; - - } elsif( $cmd eq 'clearGroup' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'createGroup'; - $string = "pid=$gid"; - - } elsif( grep { $_ eq $cmd } ("play", "stop", "pause", "next", "prev", "channel", "channelUp", "channelDown", "playlist" ) ) { - + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + $heosCmd = 'setGroupMute'; + $action = "state=$args[0]"; + + } + elsif ( $cmd eq 'volume' ) { + return "usage: $cmd 0-100" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 100 + || $args[0] < 0 ); + + $heosCmd = 'setGroupVolume'; + $action = "level=$args[0]"; + + } + elsif ( $cmd eq 'volumeUp' ) { + return "usage: $cmd 0-10" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 10 + || $args[0] < 1 ); + + $heosCmd = 'GroupVolumeUp'; + $action = "step=$args[0]"; + + } + elsif ( $cmd eq 'volumeDown' ) { + return "usage: $cmd 0-10" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 10 + || $args[0] < 1 ); + + $heosCmd = 'groupVolumeDown'; + $action = "step=$args[0]"; + + } + elsif ( $cmd eq 'clearGroup' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'createGroup'; + $string = "pid=$gid"; + + } + elsif ( + grep { $_ eq $cmd } ( + "play", "stop", "pause", "next", + "prev", "channel", "channelUp", "channelDown", + "playlist" + ) + ) + { + #ab hier Playerbefehle emuliert - $string = "pid=$gid"; - - if( $cmd eq 'repeat' ) { - return "usage: repeat one,all,off" if( @args != 1 ); - - $heosCmd = 'setPlayMode'; - $rvalue = 'on_'.$args[0]; - $rvalue = 'off' if($rvalue eq 'on_off'); - $action = "repeat=$rvalue&shuffle=".ReadingsVal($name,'shuffle','off'); - - } elsif( $cmd eq 'shuffle' ) { - return "usage: shuffle on,off" if( @args != 1 ); + $string = "pid=$gid"; - $heosCmd = 'setPlayMode'; - $rvalue = 'on_'.ReadingsVal($name,'repeat','off'); - $rvalue = 'off' if($rvalue eq 'on_off'); - $action = "repeat=$rvalue&shuffle=$args[0]"; - - } elsif( $cmd eq 'play' ) { - return "usage: play" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - - } elsif( $cmd eq 'stop' ) { - return "usage: stop" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - - } elsif( $cmd eq 'pause' ) { - return "usage: pause" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - } elsif( $cmd eq 'next' ) { - return "usage: next" if( @args != 0 ); - - $heosCmd = 'playNext'; - - } elsif( $cmd eq 'prev' ) { - return "usage: prev" if( @args != 0 ); - - $heosCmd = 'playPrev'; - - } elsif ( $cmd =~ /channel/ ) { + if ( $cmd eq 'repeat' ) { + return "usage: repeat one,all,off" if ( @args != 1 ); + + $heosCmd = 'setPlayMode'; + $rvalue = 'on_' . $args[0]; + $rvalue = 'off' if ( $rvalue eq 'on_off' ); + $action = "repeat=$rvalue&shuffle=" + . ReadingsVal( $name, 'shuffle', 'off' ); + + } + elsif ( $cmd eq 'shuffle' ) { + return "usage: shuffle on,off" if ( @args != 1 ); + + $heosCmd = 'setPlayMode'; + $rvalue = 'on_' . ReadingsVal( $name, 'repeat', 'off' ); + $rvalue = 'off' if ( $rvalue eq 'on_off' ); + $action = "repeat=$rvalue&shuffle=$args[0]"; + + } + elsif ( $cmd eq 'play' ) { + return "usage: play" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + + } + elsif ( $cmd eq 'stop' ) { + return "usage: stop" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + + } + elsif ( $cmd eq 'pause' ) { + return "usage: pause" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + } + elsif ( $cmd eq 'next' ) { + return "usage: next" if ( @args != 0 ); + + $heosCmd = 'playNext'; + + } + elsif ( $cmd eq 'prev' ) { + return "usage: prev" if ( @args != 0 ); + + $heosCmd = 'playPrev'; + + } + elsif ( $cmd =~ /channel/ ) { + + my $favorit = ReadingsVal( $name, "channel", 1 ); + + $favoritcount = scalar( @{ $hash->{IODev}{helper}{favorites} } ) + if ( defined $hash->{IODev}{helper}{favorites} ); + $heosCmd = 'playPresetStation'; - my $favorit = ReadingsVal($name,"channel", 1); - - $favoritcount = scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} ); - $heosCmd = 'playPresetStation'; - if ( $cmd eq 'channel' ) { - return "usage: $cmd 1-$favoritcount" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > $favoritcount || $args[0] < 1); + return "usage: $cmd 1-$favoritcount" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > $favoritcount + || $args[0] < 1 ); - $action = "preset=$args[0]"; + $action = "preset=$args[0]"; - } elsif( $cmd eq 'channelUp' ) { - return "usage: $cmd" if( @args != 0 ); + } + elsif ( $cmd eq 'channelUp' ) { + return "usage: $cmd" if ( @args != 0 ); ++$favorit; if ( $favorit > $favoritcount ) { - if ( AttrVal($name, 'channelring', 0) == 1 ) { - + if ( AttrVal( $name, 'channelring', 0 ) == 1 ) { + $favorit = 1; - - } else { - + + } + else { + $favorit = $favoritcount; - + } } - - $action = "preset=".$favorit; - } elsif( $cmd eq 'channelDown' ) { - return "usage: $cmd" if( @args != 0 ); + $action = "preset=" . $favorit; + + } + elsif ( $cmd eq 'channelDown' ) { + return "usage: $cmd" if ( @args != 0 ); --$favorit; if ( $favorit <= 0 ) { - if ( AttrVal($name, 'channelring', 0) == 1 ) { - + if ( AttrVal( $name, 'channelring', 0 ) == 1 ) { + $favorit = $favoritcount; - - } else { - + + } + else { + $favorit = 1; } } - - $action = "preset=".$favorit; + + $action = "preset=" . $favorit; } - - } elsif ( $cmd =~ /Playlist/ ) { - - my @cids = map { $_->{cid} } grep { $_->{name} =~ /\Q$args[0]\E/i } (@{ $hash->{IODev}{helper}{playlists} }); + + } + elsif ( $cmd =~ /Playlist/ ) { + + my @cids = + map { $_->{cid} } + grep { $_->{name} =~ /\Q$args[0]\E/i } + ( @{ $hash->{IODev}{helper}{playlists} } ); if ( scalar @args == 1 && scalar @cids > 0 ) { if ( $cmd eq 'playPlaylist' ) { - $heosCmd = $cmd; - $action = "sid=1025&cid=$cids[0]&aid=4"; - - } elsif ( $cmd eq 'deletePlaylist' ) { - - $heosCmd = $cmd; - $action = "cid=$cids[0]"; - $string = "sid=1025"; + $heosCmd = $cmd; + $action = "sid=1025&cid=$cids[0]&aid=4"; + } - } else { - - IOWrite($hash,'browseSource','sid=1025'); - my @playlists = map { $_->{name} } (@{ $hash->{IODev}{helper}{playlists}}); - return "usage: $cmd ".join(",",@playlists); + elsif ( $cmd eq 'deletePlaylist' ) { + + $heosCmd = $cmd; + $action = "cid=$cids[0]"; + $string = "sid=1025"; + } + } + else { + + IOWrite( $hash, 'browseSource', 'sid=1025' ); + my @playlists = + map { $_->{name} } ( @{ $hash->{IODev}{helper}{playlists} } ); + return "usage: $cmd " . join( ",", @playlists ); } } - } else { - - my $list = "getGroupInfo:noArg mute:on,off volume:slider,0,5,100 volumeUp:slider,0,1,10 volumeDown:slider,0,1,10 clearGroup:noArg repeat:one,all,off shuffle:on,off play:noArg stop:noArg pause:noArg next:noArg prev:noArg channelUp:noArg channelDown:noArg "; + } + else { + + my $list = +"getGroupInfo:noArg mute:on,off volume:slider,0,5,100 volumeUp:slider,0,1,10 volumeDown:slider,0,1,10 clearGroup:noArg repeat:one,all,off shuffle:on,off play:noArg stop:noArg pause:noArg next:noArg prev:noArg channelUp:noArg channelDown:noArg "; + + $list .= + " channel:slider,1,1," + . scalar( @{ $hash->{IODev}{helper}{favorites} } ) + if ( defined $hash->{IODev}{helper}{favorites} ); - $list .= " channel:slider,1,1,".scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} ); - if ( defined $hash->{IODev}{helper}{playlists} ) { - - my @playlists = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\ /g; $n{name} } (@{ $hash->{IODev}{helper}{playlists}}); - $list .= " playlist:".join(",",@playlists) if( scalar @playlists > 0 ); + + my @playlists = map { + my %n; + $n{name} = $_->{name}; + $n{name} =~ s/\s+/\ /g; + $n{name} + } ( @{ $hash->{IODev}{helper}{playlists} } ); + $list .= " playlist:" . join( ",", @playlists ) + if ( scalar @playlists > 0 ); } - + return "Unknown argument $cmd, choose one of $list"; } - - $string .= "&$action" if( defined($action)); - IOWrite($hash,"$heosCmd","$string"); - Log3 $name, 4, "HEOSGroup ($name) - IOWrite: $heosCmd $string IODevHash=$hash->{IODev}"; - return undef; + + $string .= "&$action" if ( defined($action) ); + IOWrite( $hash, "$heosCmd", "$string" ); + Log3 $name, 4, + "HEOSGroup ($name) - IOWrite: $heosCmd $string IODevHash=$hash->{IODev}"; + + return; } -sub HEOSGroup_Parse($$) { - - my ($io_hash,$json) = @_; - my $name = $io_hash->{NAME}; +sub HEOSGroup_Parse { + my ( $io_hash, $json ) = @_; + + my $name = $io_hash->{NAME}; my $gid; my $decode_json; my $code; - - $decode_json = eval{decode_json(encode_utf8($json))}; - if($@){ + $decode_json = eval { decode_json( encode_utf8($json) ) }; + if ($@) { Log3 $name, 3, "HEOSGroup ($name) - JSON error while request: $@"; return; } - + Log3 $name, 4, "HEOSGroup ($name) - ParseFn wurde aufgerufen"; - if( defined($decode_json->{gid}) ) { - - $gid = $decode_json->{gid}; - $code = abs($gid); - $code = $io_hash->{NAME} ."-". $code if( defined($io_hash->{NAME}) ); - - - if( my $hash = $modules{HEOSGroup}{defptr}{$code} ) { - - IOWrite($hash,'getGroupInfo',"gid=$hash->{GID}"); + if ( defined( $decode_json->{gid} ) ) { + + $gid = $decode_json->{gid}; + $code = abs($gid); + $code = $io_hash->{NAME} . "-" . $code + if ( defined( $io_hash->{NAME} ) ); + + if ( my $hash = $modules{HEOSGroup}{defptr}{$code} ) { + + IOWrite( $hash, 'getGroupInfo', "gid=$hash->{GID}" ); readingsSingleUpdate( $hash, "state", "on", 1 ); - Log3 $hash->{NAME}, 4, "HEOSGroup ($hash->{NAME}) - find logical device: $hash->{NAME}"; - Log3 $hash->{NAME}, 4, "HEOSGroup ($hash->{NAME}) - find GID in root from decode_json"; + Log3 $hash->{NAME}, 4, + "HEOSGroup ($hash->{NAME}) - find logical device: $hash->{NAME}"; + Log3 $hash->{NAME}, 4, + "HEOSGroup ($hash->{NAME}) - find GID in root from decode_json"; return $hash->{NAME}; - - } else { - - my $devname = "HEOSGroup".abs($gid); + + } + else { + + my $devname = "HEOSGroup" . abs($gid); return "UNDEFINED $devname HEOSGroup $gid IODev=$name"; } - - } else { - - my %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); - - $gid = $message{pid} if( defined($message{pid}) ); - $gid = $message{gid} if( defined($message{gid}) ); - $gid = $decode_json->{payload}{gid} if( defined($decode_json->{payload}{gid}) ); + + } + else { + + my %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); + + $gid = $message{pid} if ( defined( $message{pid} ) ); + $gid = $message{gid} if ( defined( $message{gid} ) ); + $gid = $decode_json->{payload}{gid} + if ( defined( $decode_json->{payload}{gid} ) ); Log3 $name, 4, "HEOSGroup ($name) - GID: $gid"; - - $code = abs($gid); - $code = $io_hash->{NAME} ."-". $code if( defined($io_hash->{NAME}) ); - - if( my $hash = $modules{HEOSGroup}{defptr}{$code} ) { - - HEOSGroup_WriteReadings($hash,$decode_json); - Log3 $hash->{NAME}, 4, "HEOSGroup ($hash->{NAME}) - find logical device: $hash->{NAME}"; + + $code = abs($gid); + $code = $io_hash->{NAME} . "-" . $code + if ( defined( $io_hash->{NAME} ) ); + + if ( my $hash = $modules{HEOSGroup}{defptr}{$code} ) { + + HEOSGroup_WriteReadings( $hash, $decode_json ); + Log3 $hash->{NAME}, 4, + "HEOSGroup ($hash->{NAME}) - find logical device: $hash->{NAME}"; return $hash->{NAME}; - - } else { - - my $devname = "HEOSGroup".abs($gid); + + } + else { + + my $devname = "HEOSGroup" . abs($gid); return "UNDEFINED $devname HEOSGroup $gid IODev=$name"; } } } -sub HEOSGroup_WriteReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; +sub HEOSGroup_WriteReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; - Log3 $name, 4, "HEOSGroup ($name) - processing data to write readings"; ############################ #### Aufbereiten der Daten soweit nötig (bei Events zum Beispiel) - my $readingsHash = HEOSGroup_PreProcessingReadings($hash,$decode_json) - if( $decode_json->{heos}{message} =~ /^gid=/ ); + my $readingsHash = HEOSGroup_PreProcessingReadings( $hash, $decode_json ) + if ( $decode_json->{heos}{message} =~ /^gid=/ ); ############################ #### schreiben der Readings readingsBeginUpdate($hash); ### Event Readings - if( ref($readingsHash) eq "HASH" ) { - - Log3 $name, 4, "HEOSGroup ($name) - response json Hash back from HEOSGroup_PreProcessingReadings"; + if ( ref($readingsHash) eq "HASH" ) { + + Log3 $name, 4, +"HEOSGroup ($name) - response json Hash back from HEOSGroup_PreProcessingReadings"; my $t; my $v; - - while( ( $t, $v ) = each (%{$readingsHash}) ) { - - readingsBulkUpdate( $hash, $t, $v ) if( defined( $v ) ); + + while ( ( $t, $v ) = each( %{$readingsHash} ) ) { + + readingsBulkUpdate( $hash, $t, $v ) if ( defined($v) ); } } #readingsBulkUpdate( $hash, 'state', 'on' ); ### GroupInfos readingsBulkUpdate( $hash, 'name', $decode_json->{payload}{name} ); - readingsBulkUpdate( $hash, 'gid', $decode_json->{payload}{gid} ); - - if ( ref($decode_json->{payload}{players}) eq "ARRAY" ) { - + readingsBulkUpdate( $hash, 'gid', $decode_json->{payload}{gid} ); + + if ( ref( $decode_json->{payload}{players} ) eq "ARRAY" ) { + my @members; - foreach my $player (@{ $decode_json->{payload}{players} }) { - - readingsBulkUpdate( $hash, 'leader', $player->{name} ) if ( $player->{role} eq "leader" ); - push( @members, $player->{name}) if ( $player->{role} eq "member" ); + foreach my $player ( @{ $decode_json->{payload}{players} } ) { + + readingsBulkUpdate( $hash, 'leader', $player->{name} ) + if ( $player->{role} eq "leader" ); + push( @members, $player->{name} ) + if ( $player->{role} eq "member" ); } - + if ( scalar @members > 1 ) { - - readingsBulkUpdate( $hash, 'member', join(",",@members) ); - - } else { - + + readingsBulkUpdate( $hash, 'member', join( ",", @members ) ); + + } + else { + readingsBulkUpdate( $hash, 'member', $members[0] ); } } - + readingsEndUpdate( $hash, 1 ); - + Log3 $name, 5, "HEOSGroup ($name) - readings set for $name"; - return undef; + + return; } ############### ### my little Helpers -sub HEOSGroup_PreProcessingReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; +sub HEOSGroup_PreProcessingReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; my $reading; my %buffer; - my %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); - + my %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); Log3 $name, 4, "HEOSGroup ($name) - preprocessing readings"; - if ( $decode_json->{heos}{command} =~ /volume_changed/ or $decode_json->{heos}{command} =~ /set_volume/ or $decode_json->{heos}{command} =~ /get_volume/ ) { - - my @value = split('&', $decode_json->{heos}{message}); + if ( $decode_json->{heos}{command} =~ /volume_changed/ + or $decode_json->{heos}{command} =~ /set_volume/ + or $decode_json->{heos}{command} =~ /get_volume/ ) + { - $buffer{'volume'} = substr($value[1],6); - $buffer{'mute'} = substr($value[2],5) if( $decode_json->{heos}{command} =~ /volume_changed/ ); - - } elsif ( $decode_json->{heos}{command} =~ /volume_up/ or $decode_json->{heos}{command} =~ /volume_down/ ) { - - my @value = split('&', $decode_json->{heos}{message}); + my @value = split( '&', $decode_json->{heos}{message} ); + + $buffer{'volume'} = substr( $value[1], 6 ); + $buffer{'mute'} = substr( $value[2], 5 ) + if ( $decode_json->{heos}{command} =~ /volume_changed/ ); + + } + elsif ($decode_json->{heos}{command} =~ /volume_up/ + or $decode_json->{heos}{command} =~ /volume_down/ ) + { + + my @value = split( '&', $decode_json->{heos}{message} ); + + $buffer{'volumeUp'} = substr( $value[1], 5 ) + if ( $decode_json->{heos}{command} =~ /volume_up/ ); + $buffer{'volumeDown'} = substr( $value[1], 5 ) + if ( $decode_json->{heos}{command} =~ /volume_down/ ); + + } + elsif ( $decode_json->{heos}{command} =~ /get_mute/ ) { + + my @value = split( '&', $decode_json->{heos}{message} ); + + $buffer{'mute'} = substr( $value[1], 6 ); + + } + else { - $buffer{'volumeUp'} = substr($value[1],5) if( $decode_json->{heos}{command} =~ /volume_up/ ); - $buffer{'volumeDown'} = substr($value[1],5) if( $decode_json->{heos}{command} =~ /volume_down/ ); - - } elsif ( $decode_json->{heos}{command} =~ /get_mute/ ) { - - my @value = split('&', $decode_json->{heos}{message}); - - $buffer{'mute'} = substr($value[1],6); - - } else { - Log3 $name, 4, "HEOSGroup ($name) - no match found"; return undef; } - + Log3 $name, 4, "HEOSGroup ($name) - Match found for decode_json"; + return \%buffer; } -sub HEOSGroup_GetGroupInfo($) { - - my $hash = shift; +sub HEOSGroup_GetGroupInfo { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSGroup_GetGroupInfo'); - IOWrite($hash,'getGroupInfo',"gid=$hash->{GID}"); + RemoveInternalTimer( $hash, 'HEOSGroup_GetGroupInfo' ); + + return IOWrite( $hash, 'getGroupInfo', "gid=$hash->{GID}" ); } -sub HEOSGroup_GetGroupVolume($) { - - my $hash = shift; +sub HEOSGroup_GetGroupVolume { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSGroup_GetGroupVolume'); - IOWrite($hash,'getGroupVolume',"gid=$hash->{GID}"); + RemoveInternalTimer( $hash, 'HEOSGroup_GetGroupVolume' ); + + return IOWrite( $hash, 'getGroupVolume', "gid=$hash->{GID}" ); } -sub HEOSGroup_GetGroupMute($) { - - my $hash = shift; +sub HEOSGroup_GetGroupMute { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSGroup_GetGroupMute'); - IOWrite($hash,'getGroupMute',"gid=$hash->{GID}"); + RemoveInternalTimer( $hash, 'HEOSGroup_GetGroupMute' ); + + return IOWrite( $hash, 'getGroupMute', "gid=$hash->{GID}" ); } - - - - - 1; - - - - =pod =item device =item summary Modul to controls the Denon multiroom soundsystem diff --git a/21_HEOSMaster.pm b/21_HEOSMaster.pm index b30a303..2948c55 100644 --- a/21_HEOSMaster.pm +++ b/21_HEOSMaster.pm @@ -38,7 +38,7 @@ # # if($@){ # Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); -# +# # readingsSingleUpdate($hash, "state", "error", 1); # # return; @@ -54,839 +54,952 @@ package main; use strict; use warnings; + #use Data::Dumper; my $missingModul = ""; eval "use Net::Telnet;1" or $missingModul .= "Net::Telnet "; -eval "use JSON;1" or $missingModul .= "JSON "; -eval "use Encode;1" or $missingModul .= "Encode "; - - - +eval "use JSON;1" or $missingModul .= "JSON "; +eval "use Encode;1" or $missingModul .= "Encode "; my $version = "1.0.3"; - my %heosCmds = ( - 'enableChangeEvents' => 'system/register_for_change_events?enable=', - 'checkAccount' => 'system/check_account', - 'signAccountIn' => 'system/sign_in?', - 'signAccountOut' => 'system/sign_out', - 'reboot' => 'system/reboot', - 'getMusicSources' => 'browse/get_music_sources', - 'browseSource' => 'browse/browse?', - 'getPlayers' => 'player/get_players', - 'getGroups' => 'group/get_groups', - 'getPlayerInfo' => 'player/get_player_info?', - 'getGroupInfo' => 'group/get_group_info?', - 'getPlayState' => 'player/get_play_state?', - 'getPlayMode' => 'player/get_play_mode?', - 'getMute' => 'player/get_mute?', - 'getGroupMute' => 'group/get_mute?', - 'getQueue' => 'player/get_queue?', - 'playQueueItem' => 'player/play_queue?', - 'clearQueue' => 'player/clear_queue?', - 'saveQueue' => 'player/save_queue?', - 'getVolume' => 'player/get_volume?', - 'getGroupVolume' => 'group/get_volume?', - 'setPlayState' => 'player/set_play_state?', - 'setPlayMode' => 'player/set_play_mode?', - 'setMute' => 'player/set_mute?', - 'setGroupMute' => 'group/set_mute?', - 'playNext' => 'player/play_next?', - 'playPrev' => 'player/play_previous?', - 'playPresetStation' => 'browse/play_preset?', - 'playInput' => 'browse/play_input?', - 'playStream' => 'browse/play_stream?', - 'playPlaylist' => 'browse/add_to_queue?', - 'renamePlaylist' => 'browse/rename_playlist?', - 'deletePlaylist' => 'browse/delete_playlist?', - 'setVolume' => 'player/set_volume?', - 'setGroupVolume' => 'group/set_volume?', - 'volumeUp' => 'player/volume_up?', - 'volumeDown' => 'player/volume_down?', - 'GroupVolumeUp' => 'group/volume_up?', - 'GroupVolumeDown' => 'group/volume_down?', - 'getNowPlayingMedia' => 'player/get_now_playing_media?', - 'eventChangeVolume' => 'event/player_volume_changed', - 'createGroup' => 'group/set_group?', - 'searchCriteria' => 'browse/get_search_criteria?', - 'search' => 'browse/search?' + 'enableChangeEvents' => 'system/register_for_change_events?enable=', + 'checkAccount' => 'system/check_account', + 'signAccountIn' => 'system/sign_in?', + 'signAccountOut' => 'system/sign_out', + 'reboot' => 'system/reboot', + 'getMusicSources' => 'browse/get_music_sources', + 'browseSource' => 'browse/browse?', + 'getPlayers' => 'player/get_players', + 'getGroups' => 'group/get_groups', + 'getPlayerInfo' => 'player/get_player_info?', + 'getGroupInfo' => 'group/get_group_info?', + 'getPlayState' => 'player/get_play_state?', + 'getPlayMode' => 'player/get_play_mode?', + 'getMute' => 'player/get_mute?', + 'getGroupMute' => 'group/get_mute?', + 'getQueue' => 'player/get_queue?', + 'playQueueItem' => 'player/play_queue?', + 'clearQueue' => 'player/clear_queue?', + 'saveQueue' => 'player/save_queue?', + 'getVolume' => 'player/get_volume?', + 'getGroupVolume' => 'group/get_volume?', + 'setPlayState' => 'player/set_play_state?', + 'setPlayMode' => 'player/set_play_mode?', + 'setMute' => 'player/set_mute?', + 'setGroupMute' => 'group/set_mute?', + 'playNext' => 'player/play_next?', + 'playPrev' => 'player/play_previous?', + 'playPresetStation' => 'browse/play_preset?', + 'playInput' => 'browse/play_input?', + 'playStream' => 'browse/play_stream?', + 'playPlaylist' => 'browse/add_to_queue?', + 'renamePlaylist' => 'browse/rename_playlist?', + 'deletePlaylist' => 'browse/delete_playlist?', + 'setVolume' => 'player/set_volume?', + 'setGroupVolume' => 'group/set_volume?', + 'volumeUp' => 'player/volume_up?', + 'volumeDown' => 'player/volume_down?', + 'GroupVolumeUp' => 'group/volume_up?', + 'GroupVolumeDown' => 'group/volume_down?', + 'getNowPlayingMedia' => 'player/get_now_playing_media?', + 'eventChangeVolume' => 'event/player_volume_changed', + 'createGroup' => 'group/set_group?', + 'searchCriteria' => 'browse/get_search_criteria?', + 'search' => 'browse/search?' ); +sub HEOSMaster_Initialize { + my $hash = shift; -# Declare functions -sub HEOSMaster_Initialize($); -sub HEOSMaster_Define($$); -sub HEOSMaster_Undef($$); -sub HEOSMaster_Set($@); -sub HEOSMaster_Open($); -sub HEOSMaster_Close($); -sub HEOSMaster_Read($); -sub HEOSMaster_Write($@); -sub HEOSMaster_Attr(@); -sub HEOSMaster_FirstRun($); -sub HEOSMaster_ResponseProcessing($$); -sub HEOSMaster_WriteReadings($$); -sub HEOSMaster_GetPlayers($); -sub HEOSMaster_EnableChangeEvents($); -sub HEOSMaster_PreProcessingReadings($$); -sub HEOSMaster_ReOpen($); -sub HEOSMaster_ReadPassword($$); -sub HEOSMaster_StorePassword($@); -sub HEOSMaster_Rename(@); -sub HEOSMaster_GetGroups($); -sub HEOSMaster_ProcessRead($$); -sub HEOSMaster_ParseMsg($$); -sub HEOSMaster_CheckAccount($); -sub HEOSMaster_Get($$@); -sub HEOSMaster_GetFavorites($); -sub HEOSMaster_GetHistory($); -sub HEOSMaster_GetInputs($); -sub HEOSMaster_GetMusicSources($); -sub HEOSMaster_GetPlaylists($); -sub HEOSMaster_GetServers($); -sub HEOSMaster_MakePlayLink($$$$$$$); -sub HEOSMaster_MakeImage($$); - - - - -sub HEOSMaster_Initialize($) { - - my ($hash) = @_; - - # Provider - $hash->{ReadFn} = "HEOSMaster_Read"; - $hash->{WriteFn} = "HEOSMaster_Write"; - $hash->{Clients} = ":HEOSPlayer:"; - $hash->{MatchList} = { "1:HEOSPlayer" => '.*{"command":."player.*|.*{"command":."event\/player.*|.*{"command":."event\/repeat_mode_changed.*|.*{"command":."event\/shuffle_mode_changed.*|.*{"command":."event\/favorites_changed.*', - "2:HEOSGroup" => '.*{"command":."group.*|.*{"command":."event\/group.*' - }; + $hash->{ReadFn} = "HEOSMaster_Read"; + $hash->{WriteFn} = "HEOSMaster_Write"; + $hash->{Clients} = ":HEOSPlayer:"; + $hash->{MatchList} = { + "1:HEOSPlayer" => +'.*{"command":."player.*|.*{"command":."event\/player.*|.*{"command":."event\/repeat_mode_changed.*|.*{"command":."event\/shuffle_mode_changed.*|.*{"command":."event\/favorites_changed.*', + "2:HEOSGroup" => '.*{"command":."group.*|.*{"command":."event\/group.*' + }; # Consumer - $hash->{SetFn} = "HEOSMaster_Set"; - $hash->{GetFn} = "HEOSMaster_Get"; - $hash->{DefFn} = "HEOSMaster_Define"; - $hash->{UndefFn} = "HEOSMaster_Undef"; - $hash->{AttrFn} = "HEOSMaster_Attr"; - $hash->{AttrList} = "disable:1 ". - "heosUsername ". - $readingFnAttributes; + $hash->{SetFn} = "HEOSMaster_Set"; + $hash->{GetFn} = "HEOSMaster_Get"; + $hash->{DefFn} = "HEOSMaster_Define"; + $hash->{UndefFn} = "HEOSMaster_Undef"; + $hash->{AttrFn} = "HEOSMaster_Attr"; + $hash->{AttrList} = "disable:1 " . "heosUsername " . $readingFnAttributes; + + foreach my $d ( sort keys %{ $modules{HEOSMaster}{defptr} } ) { - foreach my $d(sort keys %{$modules{HEOSMaster}{defptr}}) { - my $hash = $modules{HEOSMaster}{defptr}{$d}; - $hash->{VERSION} = $version; + $hash->{VERSION} = $version; } } -sub HEOSMaster_Define($$) { - +sub HEOSMaster_Define { my ( $hash, $def ) = @_; + my @a = split( "[ \t][ \t]*", $def ); - - return "too few parameters: define HEOSMaster " if( @a != 3 ); - return "Cannot define a HEOS device. Perl modul $missingModul is missing." if ( $missingModul ); - - my $name = $a[0]; - my $host = $a[2]; + return "too few parameters: define HEOSMaster " if ( @a != 3 ); + return "Cannot define a HEOS device. Perl modul $missingModul is missing." + if ($missingModul); - $hash->{HOST} = $host; - $hash->{VERSION} = $version; + my $name = $a[0]; + my $host = $a[2]; + $hash->{HOST} = $host; + $hash->{VERSION} = $version; Log3 $name, 3, "HEOSMaster ($name) - defined with host $host"; - $attr{$name}{room} = "HEOS" if( !defined( $attr{$name}{room} ) ); + $attr{$name}{room} = "HEOS" if ( !defined( $attr{$name}{room} ) ); readingsBeginUpdate($hash); - readingsBulkUpdate($hash,'state','Initialized'); - readingsBulkUpdate($hash,'enableChangeEvents', 'off'); - readingsEndUpdate($hash,1); + readingsBulkUpdate( $hash, 'state', 'Initialized' ); + readingsBulkUpdate( $hash, 'enableChangeEvents', 'off' ); + readingsEndUpdate( $hash, 1 ); + + if ($init_done) { - if( $init_done ) { - HEOSMaster_FirstRun($hash); - - } else { - - InternalTimer( gettimeofday()+15, 'HEOSMaster_FirstRun', $hash, 0 ) if( ($hash->{HOST}) ); + } - + else { + + InternalTimer( gettimeofday() + 15, 'HEOSMaster_FirstRun', $hash, 0 ) + if ( ( $hash->{HOST} ) ); + } + $modules{HEOSMaster}{defptr}{$host} = $hash; - return undef; + + return; } -sub HEOSMaster_Undef($$) { - +sub HEOSMaster_Undef { my ( $hash, $arg ) = @_; + my $host = $hash->{HOST}; my $name = $hash->{NAME}; - HEOSMaster_Close($hash); - delete $modules{HEOSMaster}{defptr}{$hash->{HOST}}; - + delete $modules{HEOSMaster}{defptr}{ $hash->{HOST} }; + Log3 $name, 3, "HEOSMaster ($name) - device $name deleted"; - return undef; + + return; } -sub HEOSMaster_Attr(@) { - +sub HEOSMaster_Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; + my $hash = $defs{$name}; my $orig = $attrVal; - - if( $attrName eq "disable" ) { - if( $cmd eq "set" and $attrVal eq "1" ) { - - readingsSingleUpdate ( $hash, "state", "disabled", 1 ); + if ( $attrName eq "disable" ) { + if ( $cmd eq "set" and $attrVal eq "1" ) { + + readingsSingleUpdate( $hash, "state", "disabled", 1 ); Log3 $name, 3, "HEOSMaster ($name) - disabled"; - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSMaster ($name) - enabled"; } } - - if( $attrName eq "disabledForIntervals" ) { - if( $cmd eq "set" ) { - + + if ( $attrName eq "disabledForIntervals" ) { + if ( $cmd eq "set" ) { + Log3 $name, 3, "HEOSMaster ($name) - enable disabledForIntervals"; - readingsSingleUpdate ( $hash, "state", "Unknown", 1 ); - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + readingsSingleUpdate( $hash, "state", "Unknown", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSMaster ($name) - delete disabledForIntervals"; } } - - return undef; + + return; } -sub HEOSMaster_Get($$@) { +sub HEOSMaster_Get { + my ( $hash, $name, @aa ) = @_; - my ($hash, $name, @aa) = @_; - my ($cmd, @args) = @aa; - my $pid = $hash->{PID}; + my ( $cmd, @args ) = @aa; + my $pid = $hash->{PID}; - - if( $cmd eq 'showAccount' ) { - return "usage: $cmd" if( @args != 0 ); - - return AttrVal($name,'heosUsername',0) . ":" .HEOSMaster_ReadPassword($hash,$name); + if ( $cmd eq 'showAccount' ) { + return "usage: $cmd" if ( @args != 0 ); + + return AttrVal( $name, 'heosUsername', 0 ) . ":" + . HEOSMaster_ReadPassword( $hash, $name ); } - + my $list = 'showAccount:noArg'; + return "Unknown argument $cmd, choose one of $list"; } -sub HEOSMaster_Set($@) { - - my ($hash, $name, $cmd, @args) = @_; - my ($arg, @params) = @args; +sub HEOSMaster_Set { + my ( $hash, $name, $cmd, @args ) = @_; + my ( $arg, @params ) = @args; + my $action; my $heosCmd; - - if($cmd eq 'reopen') { - return "usage: $cmd" if( @args != 0 ); - + if ( $cmd eq 'reopen' ) { + return "usage: $cmd" if ( @args != 0 ); + HEOSMaster_ReOpen($hash); return undef; - - } elsif($cmd eq 'getPlayers') { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'getPlayers'; - $action = undef; - - } elsif($cmd eq 'getGroups') { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'getGroups'; - $action = undef; - - } elsif($cmd eq 'enableChangeEvents') { + + } + elsif ( $cmd eq 'getPlayers' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'getPlayers'; + $action = undef; + + } + elsif ( $cmd eq 'getGroups' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'getGroups'; + $action = undef; + + } + elsif ( $cmd eq 'enableChangeEvents' ) { my $param = "on|off"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - $heosCmd = $cmd; - $action = $args[0]; - - } elsif($cmd eq 'checkAccount') { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - $action = undef; - - } elsif($cmd eq 'signAccount') { + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + $heosCmd = $cmd; + $action = $args[0]; + + } + elsif ( $cmd eq 'checkAccount' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + $action = undef; + + } + elsif ( $cmd eq 'signAccount' ) { my $param = "In|Out"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - return "please set account informattion first" if(AttrVal($name,'heosUsername','none') eq 'none'); - $heosCmd = $cmd . $args[0]; - $action = 'un='. AttrVal($name,'heosUsername','none') . '&pw=' . HEOSMaster_ReadPassword($hash,$name) if($args[0] eq 'In'); - - } elsif($cmd eq 'password') { - return "usage: $cmd" if( @args != 1 ); - + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + return "please set account informattion first" + if ( AttrVal( $name, 'heosUsername', 'none' ) eq 'none' ); + $heosCmd = $cmd . $args[0]; + $action = 'un=' + . AttrVal( $name, 'heosUsername', 'none' ) . '&pw=' + . HEOSMaster_ReadPassword( $hash, $name ) + if ( $args[0] eq 'In' ); + + } + elsif ( $cmd eq 'password' ) { + return "usage: $cmd" if ( @args != 1 ); + return HEOSMaster_StorePassword( $hash, $name, $args[0] ); - - } elsif($cmd eq 'reboot') { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - $action = undef; - - } else { - - my $list = ""; - $list .= "reopen:noArg getPlayers:noArg getGroups:noArg enableChangeEvents:on,off checkAccount:noArg signAccount:In,Out password reboot"; + + } + elsif ( $cmd eq 'reboot' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + $action = undef; + + } + else { + + my $list = ""; + $list .= +"reopen:noArg getPlayers:noArg getGroups:noArg enableChangeEvents:on,off checkAccount:noArg signAccount:In,Out password reboot"; return "Unknown argument $cmd, choose one of $list"; } - - HEOSMaster_Write($hash,$heosCmd,$action,undef); + + return HEOSMaster_Write( $hash, $heosCmd, $action, undef ); } -sub HEOSMaster_Open($) { - - my $hash = shift; - my $name = $hash->{NAME}; - my $host = $hash->{HOST}; - my $port = 1255; - my $timeout = 0.1; - my $user = AttrVal($name,'heosUsername',undef); - my $password = HEOSMaster_ReadPassword($hash,$name); +sub HEOSMaster_Open { + my $hash = shift; + + my $name = $hash->{NAME}; + my $host = $hash->{HOST}; + my $port = 1255; + my $timeout = 0.1; + my $user = AttrVal( $name, 'heosUsername', undef ); + my $password = HEOSMaster_ReadPassword( $hash, $name ); - Log3 $name, 4, "HEOSMaster ($name) - Build socket connection"; - my $socket = new Net::Telnet ( Host=>$host, - Port => $port, - Timeout=>$timeout, - Errmode=>'return') - or return Log3 $name, 3, "HEOSMaster ($name) Couldn't connect to $host:$port"; + my $socket = new Net::Telnet( + Host => $host, + Port => $port, + Timeout => $timeout, + Errmode => 'return' + ) + or return Log3 $name, 3, + "HEOSMaster ($name) Couldn't connect to $host:$port"; - $hash->{FD} = $socket->fileno(); - $hash->{CD} = $socket; # sysread / close won't work on fileno + $hash->{FD} = $socket->fileno(); + $hash->{CD} = $socket; # sysread / close won't work on fileno $selectlist{$name} = $hash; - readingsSingleUpdate($hash, 'state', 'connected', 1 ); + readingsSingleUpdate( $hash, 'state', 'connected', 1 ); Log3 $name, 4, "HEOSMaster ($name) - Socket Connected"; #hinzugefügt laut Protokoll 2.1.1 Initsequenz - HEOSMaster_Write($hash,'enableChangeEvents','off',undef); + HEOSMaster_Write( $hash, 'enableChangeEvents', 'off', undef ); Log3 $name, 4, "HEOSMaster ($name) - set enableChangeEvents off"; #hinzugefügt laut Protokoll 2.1.1 Initsequenz - if( defined($user) and defined($password) ) { - - HEOSMaster_Write($hash,'signAccountIn',"un=$user&pw=$password",undef); + if ( defined($user) and defined($password) ) { + + HEOSMaster_Write( $hash, 'signAccountIn', "un=$user&pw=$password", + undef ); Log3 $name, 4, "HEOSMaster ($name) - sign in"; } - + HEOSMaster_GetPlayers($hash); - InternalTimer( gettimeofday()+1, 'HEOSMaster_EnableChangeEvents', $hash, 0 ); - InternalTimer( gettimeofday()+2, 'HEOSMaster_GetMusicSources', $hash, 0 ); - InternalTimer( gettimeofday()+3, 'HEOSMaster_GetGroups', $hash, 0 ); - InternalTimer( gettimeofday()+10, 'HEOSMaster_GetPlayers', $hash, 0 ); + + InternalTimer( + gettimeofday() + 1, + 'HEOSMaster_EnableChangeEvents', + $hash, 0 + ); + + InternalTimer( gettimeofday() + 2, 'HEOSMaster_GetMusicSources', $hash, 0 ); + InternalTimer( gettimeofday() + 3, 'HEOSMaster_GetGroups', $hash, 0 ); + InternalTimer( gettimeofday() + 10, 'HEOSMaster_GetPlayers', $hash, 0 ); + + return; } -sub HEOSMaster_Close($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_Close { + my $hash = shift; - - return if( !$hash->{CD} ); - - close($hash->{CD}) if($hash->{CD}); - delete($hash->{FD}); - delete($hash->{CD}); - delete($selectlist{$name}); - - readingsSingleUpdate($hash, 'state', 'not connected', 1 ); + my $name = $hash->{NAME}; + + return if ( !$hash->{CD} ); + + close( $hash->{CD} ) if ( $hash->{CD} ); + delete( $hash->{FD} ); + delete( $hash->{CD} ); + delete( $selectlist{$name} ); + + readingsSingleUpdate( $hash, 'state', 'not connected', 1 ); + + return; } -sub HEOSMaster_ReOpen($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_ReOpen { + + my $hash = shift; + my $name = $hash->{NAME}; - HEOSMaster_Close($hash); - HEOSMaster_Open($hash) if( !$hash->{CD} or !defined($hash->{CD}) ); + HEOSMaster_Open($hash) if ( !$hash->{CD} or !defined( $hash->{CD} ) ); + + return; } -sub HEOSMaster_Write($@) { - - my ($hash,$heosCmd,$value,$blocking) = @_; - my $name = $hash->{NAME}; - my $string = "heos://$heosCmds{$heosCmd}"; +sub HEOSMaster_Write { + my ( $hash, $heosCmd, $value, $blocking ) = @_; - if( defined($value) ) { - - $string .= "${value}" if( $value ne '&' ); + my $name = $hash->{NAME}; + my $string = "heos://$heosCmds{$heosCmd}"; + + if ( defined($value) ) { + + $string .= "${value}" if ( $value ne '&' ); } - + if ( defined $blocking ) { - + my $idx = $blocking->{cl}{LASTACCESS}; $hash->{helper}{blocking}{$idx} = $blocking; $string .= "&SEQUENCE=$idx"; } - - $string .= "\r\n"; + + $string .= "\r\n"; Log3 $name, 4, "HEOSMaster ($name) - WriteFn called"; - + return Log3 $name, 4, "HEOSMaster ($name) - socket not connected" - unless($hash->{CD}); - + unless ( $hash->{CD} ); + Log3 $name, 5, "HEOSMaster ($name) - $string"; - syswrite($hash->{CD}, $string); - - return undef; + syswrite( $hash->{CD}, $string ); + + return; } -sub HEOSMaster_Read($) { - +sub HEOSMaster_Read { my $hash = shift; + my $name = $hash->{NAME}; my $len; my $buf; - Log3 $name, 4, "HEOSMaster ($name) - ReadFn gestartet"; - $len = sysread($hash->{CD},$buf,1024); - - if( !defined($len) || !$len ) { - + $len = sysread( $hash->{CD}, $buf, 1024 ); + + if ( !defined($len) || !$len ) { + Log3 $name, 5, "HEOSMaster ($name) - connection closed by remote Host"; HEOSMaster_Close($hash); return; } - - unless( defined $buf) { - + + unless ( defined $buf ) { + Log3 $name, 3, "HEOSMaster ($name) - no data received"; return; } - - Log3 $name, 5, "HEOSMaster ($name) - received buffer data, start HEOSMaster_ProcessRead: $buf"; - HEOSMaster_ProcessRead($hash,$buf); + + Log3 $name, 5, +"HEOSMaster ($name) - received buffer data, start HEOSMaster_ProcessRead: $buf"; + + return HEOSMaster_ProcessRead( $hash, $buf ); } -sub HEOSMaster_ProcessRead($$) { - - my ($hash, $data) = @_; - my $name = $hash->{NAME}; +sub HEOSMaster_ProcessRead { + + my ( $hash, $data ) = @_; + my $name = $hash->{NAME}; my $buffer = ''; - Log3 $name, 4, "HEOSMaster ($name) - process read"; + #include previous partial message - - if(defined($hash->{PARTIAL}) && $hash->{PARTIAL}) { - + + if ( defined( $hash->{PARTIAL} ) && $hash->{PARTIAL} ) { + Log3 $name, 5, "HEOSMaster ($name) - PARTIAL: " . $hash->{PARTIAL}; $buffer = $hash->{PARTIAL}; - - } else { - + + } + else { + Log3 $name, 4, "HEOSMaster ($name) - No PARTIAL buffer"; } - - Log3 $name, 5, "HEOSMaster ($name) - Incoming data: " . $data; - - $buffer = $buffer . $data; - - Log3 $name, 5, "HEOSMaster ($name) - Current processing buffer (PARTIAL + incoming data): " . $buffer; - - my ($json,$tail) = HEOSMaster_ParseMsg($hash, $buffer); - #processes all complete messages - - while($json) { - - $hash->{LAST_RECV} = time(); - Log3 $name, 5, "HEOSMaster ($name) - Decoding JSON message. Length: " . length($json) . " Content: " . $json; - my $obj = eval{decode_json($json)}; - if($@){ + Log3 $name, 5, "HEOSMaster ($name) - Incoming data: " . $data; + + $buffer = $buffer . $data; + + Log3 $name, 5, +"HEOSMaster ($name) - Current processing buffer (PARTIAL + incoming data): " + . $buffer; + + my ( $json, $tail ) = HEOSMaster_ParseMsg( $hash, $buffer ); + + #processes all complete messages + + while ($json) { + + $hash->{LAST_RECV} = time(); + Log3 $name, 5, + "HEOSMaster ($name) - Decoding JSON message. Length: " + . length($json) + . " Content: " + . $json; + + my $obj = eval { decode_json($json) }; + if ($@) { Log3 $name, 3, "HEOSMaster ($name) - JSON error while request: $@"; return; } - - if(defined($obj->{heos})) { - - HEOSMaster_ResponseProcessing($hash,$json); - Log3 $name, 4, "HEOSMaster ($name) - starte HEOSMaster_ResponseProcessing"; - - } elsif(defined($obj->{error})) { - - Log3 $name, 3, "HEOSMaster ($name) - Received error message: " . $json; + + if ( defined( $obj->{heos} ) ) { + + HEOSMaster_ResponseProcessing( $hash, $json ); + Log3 $name, 4, + "HEOSMaster ($name) - starte HEOSMaster_ResponseProcessing"; + } - - ($json,$tail) = HEOSMaster_ParseMsg($hash, $tail); + elsif ( defined( $obj->{error} ) ) { + + Log3 $name, 3, + "HEOSMaster ($name) - Received error message: " . $json; + } + + ( $json, $tail ) = HEOSMaster_ParseMsg( $hash, $tail ); } - + $hash->{PARTIAL} = $tail; Log3 $name, 5, "HEOSMaster ($name) - Tail: " . $tail; Log3 $name, 5, "HEOSMaster ($name) - PARTIAL: " . $hash->{PARTIAL}; + return; } -sub HEOSMaster_ResponseProcessing($$) { - - my ($hash,$json) = @_; - my $name = $hash->{NAME}; +sub HEOSMaster_ResponseProcessing { + my ( $hash, $json ) = @_; + + my $name = $hash->{NAME}; my $decode_json; my %message; - Log3 $name, 5, "HEOSMaster ($name) - JSON String: $json"; return Log3 $name, 3, "HEOSMaster ($name) - empty answer received" - unless( defined($json)); + unless ( defined($json) ); Log3 $name, 4, "HEOSMaster ($name) - JSON detected!"; - $decode_json = eval{decode_json(encode_utf8($json))}; - if($@){ + $decode_json = eval { decode_json( encode_utf8($json) ) }; + if ($@) { Log3 $name, 3, "HEOSMaster ($name) - JSON error while request: $@"; return; } return Log3 $name, 3, "HEOSMaster ($name) - decode_json has no Hash" - unless(ref($decode_json) eq "HASH"); + unless ( ref($decode_json) eq "HASH" ); return Log3 $name, 4, "HEOSMaster ($name) - heos worked" - if( defined($decode_json->{heos}{message}) && $decode_json->{heos}{message} =~ /command\sunder\sprocess/ ); + if ( defined( $decode_json->{heos}{message} ) + && $decode_json->{heos}{message} =~ /command\sunder\sprocess/ ); - if( defined($decode_json->{heos}{result}) or $decode_json->{heos}{command} =~ /^system/ ) { - - HEOSMaster_WriteReadings($hash,$decode_json); + if ( defined( $decode_json->{heos}{result} ) + or $decode_json->{heos}{command} =~ /^system/ ) + { + + HEOSMaster_WriteReadings( $hash, $decode_json ); Log3 $name, 4, "HEOSMaster ($name) - call Sub HEOSMaster_WriteReadings"; } - if( defined($decode_json->{heos}{message}) ) { + if ( defined( $decode_json->{heos}{message} ) ) { - %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); + %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); - return Log3 $name, 4, "HEOSMaster ($name) - general error ID $message{eid} - $message{text}" - if( defined($message{eid}) ); + return Log3 $name, 4, + "HEOSMaster ($name) - general error ID $message{eid} - $message{text}" + if ( defined( $message{eid} ) ); } #Player neu einlesen - if( $decode_json->{heos}{command} =~ /^event\/players_changed/ ) { + if ( $decode_json->{heos}{command} =~ /^event\/players_changed/ ) { - HEOSMaster_Write($hash,'getPlayers',undef,undef); + HEOSMaster_Write( $hash, 'getPlayers', undef, undef ); return Log3 $name, 4, "HEOSMaster ($name) - player changed"; } #Gruppen neu einlesen - if( $decode_json->{heos}{command} =~ /^event\/groups_changed/ ) { + if ( $decode_json->{heos}{command} =~ /^event\/groups_changed/ ) { + HEOSMaster_Write( $hash, 'getGroups', undef, undef ); - HEOSMaster_Write($hash,'getGroups',undef,undef); #Player neu einlesen da Stereopaare sonst nicht erkannt werden - InternalTimer( gettimeofday()+3, 'HEOSMaster_GetPlayers', $hash, 0 ); + InternalTimer( gettimeofday() + 3, 'HEOSMaster_GetPlayers', $hash, 0 ); return Log3 $name, 4, "HEOSMaster ($name) - groups changed"; } #Quellen neu einlesen - if( $decode_json->{heos}{command} =~ /^event\/sources_changed/ ) { - - HEOSMaster_Write($hash,'getMusicSources',undef,undef); + if ( $decode_json->{heos}{command} =~ /^event\/sources_changed/ ) { + + HEOSMaster_Write( $hash, 'getMusicSources', undef, undef ); return Log3 $name, 4, "HEOSMaster ($name) - source changed"; } #User neu einlesen - if( $decode_json->{heos}{command} =~ /^event\/user_changed/ ) { - - HEOSMaster_Write($hash,'checkAccount',undef,undef); + if ( $decode_json->{heos}{command} =~ /^event\/user_changed/ ) { + + HEOSMaster_Write( $hash, 'checkAccount', undef, undef ); return Log3 $name, 4, "HEOSMaster ($name) - user changed"; } #Queue für Player neu einlesen if ( $decode_json->{heos}{command} =~ /^event\/player_queue_changed/ ) { - HEOSMaster_Write($hash,'getQueue',"pid=$message{pid}",undef); + HEOSMaster_Write( $hash, 'getQueue', "pid=$message{pid}", undef ); return Log3 $name, 3, "HEOSMaster ($name) - queue changed"; - + } #Playlisten neu einlesen da Queue als Playlist gespeichert wurde - if( $decode_json->{heos}{command} =~ /^player\/save_queue/ ) { - - HEOSMaster_Write($hash,'browseSource','sid=1025',undef); - return Log3 $name, 4, "HEOSMaster ($name) - playlist changed"; + if ( $decode_json->{heos}{command} =~ /^player\/save_queue/ ) { + + HEOSMaster_Write( $hash, 'browseSource', 'sid=1025', undef ); + return Log3 $name, 4, "HEOSMaster ($name) - playlist changed"; } - if( $decode_json->{heos}{command} =~ /^browse\/get_music_sources/ and ref($decode_json->{payload}) eq "ARRAY" and scalar(@{$decode_json->{payload}}) > 0) { + if ( $decode_json->{heos}{command} =~ /^browse\/get_music_sources/ + and ref( $decode_json->{payload} ) eq "ARRAY" + and scalar( @{ $decode_json->{payload} } ) > 0 ) + { #liest nur die Onlinequellen der Rest wird extra eingelesen $hash->{helper}{sources} = []; my $i = 4; - foreach my $payload ( @{$decode_json->{payload}} ) { - if( $payload->{sid} eq "1024" ) { - - $i += 2; - InternalTimer( gettimeofday()+$i, 'HEOSMaster_GetServers', $hash, 0 ); - Log3 $name, 4, "HEOSMaster ($name) - GetServers in $i seconds"; - - } elsif( $payload->{sid} eq "1025" ) { - - $i += 2; - InternalTimer( gettimeofday()+$i, 'HEOSMaster_GetPlaylists', $hash, 0 ); - Log3 $name, 4, "HEOSMaster ($name) - GetPlaylists in $i seconds"; - - } elsif( $payload->{sid} eq "1026" ) { - - $i += 2; - InternalTimer( gettimeofday()+$i, 'HEOSMaster_GetHistory', $hash, 0 ); - Log3 $name, 4, "HEOSMaster ($name) - GetHistory in $i seconds"; - - } elsif( $payload->{sid} eq "1027" ) { - - $i += 2; - InternalTimer( gettimeofday()+$i, 'HEOSMaster_GetInputs', $hash, 0 ); - Log3 $name, 4, "HEOSMaster ($name) - GetInputs in $i seconds"; - - } elsif( $payload->{sid} eq "1028" ) { - - $i += 2; - InternalTimer( gettimeofday()+$i, 'HEOSMaster_GetFavorites', $hash, 0 ); - Log3 $name, 4, "HEOSMaster ($name) - GetFavorites in $i seconds"; - - } else { - - #Onlinedienste - push( @{$hash->{helper}{sources}},$payload); - Log3 $name, 4, "HEOSMaster ($name) - GetRadioSource {$payload->{name} with sid $payload->{sid}"; + foreach my $payload ( @{ $decode_json->{payload} } ) { + if ( $payload->{sid} eq "1024" ) { - foreach my $source (@{$hash->{helper}{sources}}) { - HEOSMaster_Write($hash,'searchCriteria','sid='.$source->{sid},undef); + $i += 2; + InternalTimer( gettimeofday() + $i, + 'HEOSMaster_GetServers', $hash, 0 ); + Log3 $name, 4, "HEOSMaster ($name) - GetServers in $i seconds"; + + } + elsif ( $payload->{sid} eq "1025" ) { + + $i += 2; + InternalTimer( gettimeofday() + $i, + 'HEOSMaster_GetPlaylists', $hash, 0 ); + Log3 $name, 4, + "HEOSMaster ($name) - GetPlaylists in $i seconds"; + + } + elsif ( $payload->{sid} eq "1026" ) { + + $i += 2; + InternalTimer( gettimeofday() + $i, + 'HEOSMaster_GetHistory', $hash, 0 ); + Log3 $name, 4, "HEOSMaster ($name) - GetHistory in $i seconds"; + + } + elsif ( $payload->{sid} eq "1027" ) { + + $i += 2; + InternalTimer( gettimeofday() + $i, + 'HEOSMaster_GetInputs', $hash, 0 ); + Log3 $name, 4, "HEOSMaster ($name) - GetInputs in $i seconds"; + + } + elsif ( $payload->{sid} eq "1028" ) { + + $i += 2; + InternalTimer( gettimeofday() + $i, + 'HEOSMaster_GetFavorites', $hash, 0 ); + Log3 $name, 4, + "HEOSMaster ($name) - GetFavorites in $i seconds"; + + } + else { + + #Onlinedienste + push( @{ $hash->{helper}{sources} }, $payload ); + Log3 $name, 4, +"HEOSMaster ($name) - GetRadioSource {$payload->{name} with sid $payload->{sid}"; + + foreach my $source ( @{ $hash->{helper}{sources} } ) { + HEOSMaster_Write( $hash, 'searchCriteria', + 'sid=' . $source->{sid}, undef ); } } } - - return Log3 $name, 3, "HEOSMaster ($name) - call Sourcebrowser"; + + return Log3 $name, 3, "HEOSMaster ($name) - call Sourcebrowser"; } - if( $decode_json->{heos}{command} =~ /^browse\/browse/ and ref($decode_json->{payload}) eq "ARRAY" and scalar(@{$decode_json->{payload}}) > 0) { + if ( $decode_json->{heos}{command} =~ /^browse\/browse/ + and ref( $decode_json->{payload} ) eq "ARRAY" + and scalar( @{ $decode_json->{payload} } ) > 0 ) + { if ( defined $message{sid} ) { - if ( defined $message{range} ) { + if ( defined $message{range} ) { $message{range} =~ s/(\d+)\,\d+/$1/; - - } else { - + + } + else { + $message{range} = 0; } - + my $start = $message{range} + $message{returned}; - if( $message{sid} eq '1024' ) { + if ( $message{sid} eq '1024' ) { #Lokal einlesen - push( @{$hash->{helper}{sources}}, map { $_->{name} .= " USB" if ( $_->{sid} < 0 ); $_ } (@{$decode_json->{payload}}) ); + push( + @{ $hash->{helper}{sources} }, + map { $_->{name} .= " USB" if ( $_->{sid} < 0 ); $_ } + ( @{ $decode_json->{payload} } ) + ); - foreach my $source (@{$hash->{helper}{sources}}) { + foreach my $source ( @{ $hash->{helper}{sources} } ) { - HEOSMaster_Write($hash,'searchCriteria','sid='.$source->{sid},undef); - Log3 $name, 3, "HEOSMaster ($name) - call Browser for searchCriteria for sid $source->{sid}"; + HEOSMaster_Write( $hash, 'searchCriteria', + 'sid=' . $source->{sid}, undef ); + Log3 $name, 3, +"HEOSMaster ($name) - call Browser for searchCriteria for sid $source->{sid}"; } - } elsif( $message{sid} eq '1025' ) { + } + elsif ( $message{sid} eq '1025' ) { #Playlisten einlesen $hash->{helper}{playlists} = [] if ( $message{range} == 0 ); - push( @{$hash->{helper}{playlists}}, (@{$decode_json->{payload}}) ); + push( + @{ $hash->{helper}{playlists} }, + ( @{ $decode_json->{payload} } ) + ); - } elsif( $message{sid} eq '1026' ) { + } + elsif ( $message{sid} eq '1026' ) { #History einlesen $hash->{helper}{history} = [] if ( $message{range} == 0 ); - push( @{$hash->{helper}{history}}, (@{$decode_json->{payload}}) ); + push( + @{ $hash->{helper}{history} }, + ( @{ $decode_json->{payload} } ) + ); - } elsif( $message{sid} eq '1027' ) { + } + elsif ( $message{sid} eq '1027' ) { #Inputs einlesen $hash->{helper}{aux} = [] if ( $message{range} == 0 ); - push( @{$hash->{helper}{aux}}, (@{$decode_json->{payload}}) ); + push( + @{ $hash->{helper}{aux} }, + ( @{ $decode_json->{payload} } ) + ); - foreach my $item (@{$decode_json->{payload}}) { + foreach my $item ( @{ $decode_json->{payload} } ) { - - HEOSMaster_Write($hash,'browseSource',"sid=$item->{sid}",undef); - Log3 $name, 3, "HEOSMaster ($name) - call Browser for Input with sid $item->{sid}"; + HEOSMaster_Write( $hash, 'browseSource', + "sid=$item->{sid}", undef ); + Log3 $name, 3, +"HEOSMaster ($name) - call Browser for Input with sid $item->{sid}"; } - } elsif( $message{sid} eq '1028' ) { - + } + elsif ( $message{sid} eq '1028' ) { + #Favoriten einlesen $hash->{helper}{favorites} = [] if ( $message{range} == 0 ); - push( @{$hash->{helper}{favorites}}, (@{$decode_json->{payload}}) ); - - if ( $start >= $message{count} ) { - - #Nachricht an die Player das sich die Favoriten geändert haben - foreach my $dev ( devspec2array("TYPE=HEOSPlayer") ) { - - $json = '{"heos": {"command": "event/favorites_changed", "message": "pid='.$defs{$dev}->{PID}.'"}}'; - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for Favorites Changed"; + push( + @{ $hash->{helper}{favorites} }, + ( @{ $decode_json->{payload} } ) + ); + + if ( $start >= $message{count} ) { + + #Nachricht an die Player das sich die Favoriten geändert haben + foreach my $dev ( devspec2array("TYPE=HEOSPlayer") ) { + + $json = +'{"heos": {"command": "event/favorites_changed", "message": "pid=' + . $defs{$dev}->{PID} . '"}}'; + Dispatch( $hash, $json, undef ); + Log3 $name, 4, +"HEOSMaster ($name) - call Dispatcher for Favorites Changed"; } } - - } else { + + } + else { #AUX Eingang des Player im Player abspeichern - if ( defined $hash->{helper}{aux} && grep( $_->{sid} =~ /^$message{sid}$/, (@{ $hash->{helper}{aux} }) ) ) { + if ( + defined $hash->{helper}{aux} + && grep( $_->{sid} =~ /^$message{sid}$/, + ( @{ $hash->{helper}{aux} } ) ) + ) + { + + my $code = abs( $message{sid} ); + $code = $hash->{NAME} . "-" . $code + if ( defined( $hash->{NAME} ) ); + + if ( my $phash = $modules{HEOSPlayer}{defptr}{$code} ) { - my $code = abs($message{sid}); - $code = $hash->{NAME} ."-". $code if( defined($hash->{NAME}) ); - - if( my $phash = $modules{HEOSPlayer}{defptr}{$code} ) { - $phash->{helper}{aux} = $decode_json->{payload}; } - $json = '{"heos": {"command": "event/player_aux_changed", "message": "pid='.$message{sid}.'"}}'; - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for AUX Changed"; - + $json = +'{"heos": {"command": "event/player_aux_changed", "message": "pid=' + . $message{sid} . '"}}'; + Dispatch( $hash, $json, undef ); + Log3 $name, 4, + "HEOSMaster ($name) - call Dispatcher for AUX Changed"; + } - + #aktuelle Medien einlesen $hash->{helper}{media} = [] if ( $message{range} == 0 ); - push( @{$hash->{helper}{media}}, (@{$decode_json->{payload}}) ); + push( + @{ $hash->{helper}{media} }, + ( @{ $decode_json->{payload} } ) + ); } - - Log3 $name, 4, "HEOSMaster ($name) - call Browser with sid $message{sid} and $message{returned} items from $message{count} items"; - - if ( $start < $message{count} ) { - - my $path = "sid=$message{sid}"; - $path .= "&cid=$message{cid}" if ( defined $message{cid} ); - $path .= "&SEQUENCE=$message{SEQUENCE}" if ( defined $message{SEQUENCE} ); - HEOSMaster_Write($hash,'browseSource',"$path&range=$start,".($start + 100),undef); - Log3 $name, 3, "HEOSMaster ($name) - call Browser with sid $message{sid} next Range from $message{returned}"; - } else { + Log3 $name, 4, +"HEOSMaster ($name) - call Browser with sid $message{sid} and $message{returned} items from $message{count} items"; + + if ( $start < $message{count} ) { + + my $path = "sid=$message{sid}"; + $path .= "&cid=$message{cid}" if ( defined $message{cid} ); + $path .= "&SEQUENCE=$message{SEQUENCE}" + if ( defined $message{SEQUENCE} ); + HEOSMaster_Write( $hash, 'browseSource', + "$path&range=$start," . ( $start + 100 ), undef ); + Log3 $name, 3, +"HEOSMaster ($name) - call Browser with sid $message{sid} next Range from $message{returned}"; + + } + else { if ( defined $message{SEQUENCE} ) { - + my $idx = $message{SEQUENCE}; - if( defined $hash->{helper}{blocking}{$idx} && $hash->{helper}{blocking}{$idx}{cl}{canAsyncOutput} ) { - + if ( defined $hash->{helper}{blocking}{$idx} + && $hash->{helper}{blocking}{$idx}{cl}{canAsyncOutput} ) + { + my @list; my $xcmd; my $xtext; my $ret; - if( $hash->{helper}{blocking}{$idx}{cl}->{TYPE} eq 'FHEMWEB' ) { + if ( $hash->{helper}{blocking}{$idx}{cl}->{TYPE} eq + 'FHEMWEB' ) + { $ret = '
'; - $ret .= '

'.$hash->{helper}{blocking}{$idx}{sourcename}.'


'; + $ret .= + '

' + . $hash->{helper}{blocking}{$idx}{sourcename} + . '


'; - } else { - - $ret .= $hash->{helper}{blocking}{$idx}{sourcename}."\n"; + } + else { + + $ret .= $hash->{helper}{blocking}{$idx}{sourcename} + . "\n"; $ret .= sprintf( "%-15s %s\n", 'key', 'title' ); } if ( $message{sid} eq "1025" ) { - - @list = (@{$hash->{helper}{playlists}}); - - } elsif ( $message{sid} eq "1026" ) { - - @list = (@{$hash->{helper}{history}}); - - } elsif ( $message{sid} eq "1027" ) { - - @list = (@{$hash->{helper}{aux}}); - - } elsif ( $message{sid} eq "1028" ) { - - @list = (@{$hash->{helper}{favorites}}); - - } else { - - @list = (@{$hash->{helper}{media}}); - + + @list = ( @{ $hash->{helper}{playlists} } ); + + } + elsif ( $message{sid} eq "1026" ) { + + @list = ( @{ $hash->{helper}{history} } ); + + } + elsif ( $message{sid} eq "1027" ) { + + @list = ( @{ $hash->{helper}{aux} } ); + + } + elsif ( $message{sid} eq "1028" ) { + + @list = ( @{ $hash->{helper}{favorites} } ); + + } + else { + + @list = ( @{ $hash->{helper}{media} } ); + } my $x = 0; foreach my $item (@list) { - $ret .= HEOSMaster_MakePlayLink($hash->{helper}{blocking}{$idx}{cl}->{TYPE}, $hash->{helper}{blocking}{$idx}{name}, \%message, $item, ++$x, 64, 64); + $ret .= HEOSMaster_MakePlayLink( + $hash->{helper}{blocking}{$idx}{cl}->{TYPE}, + $hash->{helper}{blocking}{$idx}{name}, + \%message, + $item, + ++$x, + 64, + 64 + ); } - - if( $hash->{helper}{blocking}{$idx}{cl}->{TYPE} eq 'FHEMWEB' ) { + if ( $hash->{helper}{blocking}{$idx}{cl}->{TYPE} eq + 'FHEMWEB' ) + { $ret .= '
'; $ret =~ s/&/&/g; $ret =~ s/'/'/g; $ret =~ s/\n/
/g; - $ret = "
$ret
" if( $ret =~ m/ / ); + $ret = "
$ret
" if ( $ret =~ m/ / ); $ret = "$ret"; } - asyncOutput( $hash->{helper}{blocking}{$idx}{cl}, $ret ); + asyncOutput( $hash->{helper}{blocking}{$idx}{cl}, + $ret ); delete $hash->{helper}{blocking}{$idx}; } } } - + return; } } - if( $decode_json->{heos}{command} =~ /^browse\/get_search_criteria/ && ref($decode_json->{payload}) eq "ARRAY" && scalar(@{$decode_json->{payload}}) > 0) { - - push( @{$hash->{helper}{search}{$message{sid}}}, (@{$decode_json->{payload}}) ); - Log3 $name, 3, "HEOSMaster ($name) - call Browser with sid $message{sid}"; + if ( $decode_json->{heos}{command} =~ /^browse\/get_search_criteria/ + && ref( $decode_json->{payload} ) eq "ARRAY" + && scalar( @{ $decode_json->{payload} } ) > 0 ) + { + + push( + @{ $hash->{helper}{search}{ $message{sid} } }, + ( @{ $decode_json->{payload} } ) + ); + Log3 $name, 3, + "HEOSMaster ($name) - call Browser with sid $message{sid}"; } - if( $decode_json->{heos}{command} =~ /^browse\/search/ ) { + if ( $decode_json->{heos}{command} =~ /^browse\/search/ ) { Log3 $name, 3, "HEOSMaster ($name) - call search for $message{sid}"; - - if ( defined $message{range} ) { - + + if ( defined $message{range} ) { + $message{range} =~ s/(\d+)\,\d+/$1/; - - } else { - - $message{range} = 0; + + } + else { + + $message{range} = 0; $hash->{helper}{searchresult} = []; } - - my $start = $message{range} + $message{returned}; - push( @{$hash->{helper}{searchresult}}, (@{$decode_json->{payload}}) ); - - if ( $start < $message{count} ) { - - HEOSMaster_Write($hash,"search","sid=$message{sid}&search=$message{search}&scid=$message{scid}",undef); - Log3 $name, 3, "HEOSMaster ($name) - call Search for $message{sid} next Range from $message{returned}"; - - } else { - if( $hash->{helper}{blocking} && $hash->{helper}{blocking}{cl}{canAsyncOutput} ) { - + my $start = $message{range} + $message{returned}; + push( + @{ $hash->{helper}{searchresult} }, + ( @{ $decode_json->{payload} } ) + ); + + if ( $start < $message{count} ) { + + HEOSMaster_Write( + $hash, + "search", + "sid=$message{sid}&search=$message{search}&scid=$message{scid}", + undef + ); + Log3 $name, 3, +"HEOSMaster ($name) - call Search for $message{sid} next Range from $message{returned}"; + + } + else { + + if ( $hash->{helper}{blocking} + && $hash->{helper}{blocking}{cl}{canAsyncOutput} ) + { + my $ret = ''; - + $ret .= sprintf( "%-35s %-10s %s\n", 'Fav', 'type', 'title' ); - + $ret .= "\n\n"; - + asyncOutput( $hash->{helper}{blocking}{cl}, $ret ); delete $hash->{helper}{blocking}; } @@ -895,132 +1008,163 @@ sub HEOSMaster_ResponseProcessing($$) { Log3 $name, 4, "HEOSMaster ($name) - call Browser for Search"; } - if( $decode_json->{heos}{command} =~ /^player/ or $decode_json->{heos}{command} =~ /^event\/player/ or $decode_json->{heos}{command} =~ /^group/ or $decode_json->{heos}{command} =~ /^event\/group/ or $decode_json->{heos}{command} =~ /^event\/repeat_mode_changed/ or $decode_json->{heos}{command} =~ /^event\/shuffle_mode_changed/ ) { + if ( $decode_json->{heos}{command} =~ /^player/ + or $decode_json->{heos}{command} =~ /^event\/player/ + or $decode_json->{heos}{command} =~ /^group/ + or $decode_json->{heos}{command} =~ /^event\/group/ + or $decode_json->{heos}{command} =~ /^event\/repeat_mode_changed/ + or $decode_json->{heos}{command} =~ /^event\/shuffle_mode_changed/ ) + { + + if ( $decode_json->{heos}{command} =~ /player\/get_players/ ) { - if( $decode_json->{heos}{command} =~ /player\/get_players/ ) { - return Log3 $name, 4, "HEOSMaster ($name) - empty ARRAY received" - unless(scalar(@{$decode_json->{payload}}) > 0); + unless ( scalar( @{ $decode_json->{payload} } ) > 0 ); my $filter = "TYPE=HEOSPlayer:FILTER=PID!="; - foreach my $payload (@{$decode_json->{payload}}) { - - $json = '{"pid": "'; - $json .= "$payload->{pid}"; - $json .= '","heos": {"command": "player/get_player_info"}}'; - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for Players"; - $filter .= $payload->{pid}."|"; + foreach my $payload ( @{ $decode_json->{payload} } ) { + + $json = '{"pid": "'; + $json .= "$payload->{pid}"; + $json .= '","heos": {"command": "player/get_player_info"}}'; + Dispatch( $hash, $json, undef ); + Log3 $name, 4, + "HEOSMaster ($name) - call Dispatcher for Players"; + $filter .= $payload->{pid} . "|"; } - chop($filter); #letztes | wieder abschneiden + chop($filter); #letztes | wieder abschneiden #alle Player ausschalten die nicht mehr im HEOS System existieren foreach my $dev ( devspec2array($filter) ) { - + my $phash = $defs{$dev}; readingsSingleUpdate( $phash, "state", "off", 1 ); } - - } elsif( $decode_json->{heos}{command} =~ /group\/get_groups/ ) { - + + } + elsif ( $decode_json->{heos}{command} =~ /group\/get_groups/ ) { + my $filter = "TYPE=HEOSGroup"; - if ( scalar(@{$decode_json->{payload}}) > 0 ) { - + if ( scalar( @{ $decode_json->{payload} } ) > 0 ) { + $filter .= ":FILTER=GID!="; - - foreach my $payload (@{$decode_json->{payload}}) { - - $json = '{"gid": "'; - $json .= "$payload->{gid}"; - $json .= '","heos": {"command": "group/get_group_info"}}'; - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for Groups"; - $filter .= $payload->{gid}."|"; + + foreach my $payload ( @{ $decode_json->{payload} } ) { + + $json = '{"gid": "'; + $json .= "$payload->{gid}"; + $json .= '","heos": {"command": "group/get_group_info"}}'; + Dispatch( $hash, $json, undef ); + Log3 $name, 4, + "HEOSMaster ($name) - call Dispatcher for Groups"; + $filter .= $payload->{gid} . "|"; } - - chop($filter); #letztes | wieder abschneiden + + chop($filter); #letztes | wieder abschneiden } - + #alle Gruppe ausschalten die nicht mehr im HEOS System existieren foreach my $dev ( devspec2array($filter) ) { - + my $ghash = $defs{$dev}; readingsSingleUpdate( $ghash, "state", "off", 1 ); - } + } - } elsif( $decode_json->{heos}{command} =~ /player\/get_player_info/ ) { # ist vielleicht verständlicher? - - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for PlayerInfo"; + } + elsif ( $decode_json->{heos}{command} =~ /player\/get_player_info/ ) + { # ist vielleicht verständlicher? - } elsif( $decode_json->{heos}{command} =~ /group\/get_group_info/ ) { # ist vielleicht verständlicher? - - Dispatch($hash,$json,undef); + Dispatch( $hash, $json, undef ); + Log3 $name, 4, + "HEOSMaster ($name) - call Dispatcher for PlayerInfo"; + + } + elsif ( $decode_json->{heos}{command} =~ /group\/get_group_info/ ) + { # ist vielleicht verständlicher? + + Dispatch( $hash, $json, undef ); Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for GroupInfo"; - } elsif( $decode_json->{heos}{command} =~ /player\/get_queue/ ) { + } + elsif ( $decode_json->{heos}{command} =~ /player\/get_queue/ ) { + + Log3 $name, 3, + "HEOSMaster ($name) - call getQueue for player $message{pid}"; + + if ( defined $message{range} ) { - Log3 $name, 3, "HEOSMaster ($name) - call getQueue for player $message{pid}"; - - if ( defined $message{range} ) { - $message{range} =~ s/(\d+)\,\d+/$1/; - - } else { - - $message{range} = 0; - $hash->{helper}{queue}{$message{pid}} = []; + } - + else { + + $message{range} = 0; + $hash->{helper}{queue}{ $message{pid} } = []; + } + my $start = $message{range} + $message{returned}; - push( @{$hash->{helper}{queue}{$message{pid}}}, (@{$decode_json->{payload}}) ); - - if ( $start < $message{count} ) { - - HEOSMaster_Write($hash,'getQueue',"pid=$message{pid}&range=$start,".($start + 100),undef); - Log3 $name, 4, "HEOSMaster ($name) - call getQueue for player pid $message{pid} next Range from $start"; + push( + @{ $hash->{helper}{queue}{ $message{pid} } }, + ( @{ $decode_json->{payload} } ) + ); - } else { + if ( $start < $message{count} ) { - my $code = abs($message{pid}); - $code = $hash->{NAME} ."-". $code if( defined($hash->{NAME}) ); - - if( my $phash = $modules{HEOSPlayer}{defptr}{$code} ) { - - $phash->{helper}{queue} = $hash->{helper}{queue}{$message{pid}}; - delete $hash->{helper}{queue}{$message{pid}}; + HEOSMaster_Write( $hash, 'getQueue', + "pid=$message{pid}&range=$start," . ( $start + 100 ), + undef ); + Log3 $name, 4, +"HEOSMaster ($name) - call getQueue for player pid $message{pid} next Range from $start"; + + } + else { + + my $code = abs( $message{pid} ); + $code = $hash->{NAME} . "-" . $code + if ( defined( $hash->{NAME} ) ); + + if ( my $phash = $modules{HEOSPlayer}{defptr}{$code} ) { + + $phash->{helper}{queue} = + $hash->{helper}{queue}{ $message{pid} }; + delete $hash->{helper}{queue}{ $message{pid} }; } - $json = '{"heos": {"command": "event/player_queue_changed", "message": "pid='.$message{pid}.'"}}'; - Dispatch($hash,$json,undef); - Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for Queue Changed"; + $json = +'{"heos": {"command": "event/player_queue_changed", "message": "pid=' + . $message{pid} . '"}}'; + Dispatch( $hash, $json, undef ); + Log3 $name, 4, + "HEOSMaster ($name) - call Dispatcher for Queue Changed"; } - } elsif( defined($message{pid}) or defined($message{gid}) ) { - - Dispatch($hash,$json,undef); + } + elsif ( defined( $message{pid} ) or defined( $message{gid} ) ) { + + Dispatch( $hash, $json, undef ); Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher"; } - + return; } - + Log3 $name, 4, "HEOSMaster ($name) - no Match for processing data"; + + return; } -sub HEOSMaster_WriteReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; - - +sub HEOSMaster_WriteReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; + ############################ #### Aufbereiten der Daten soweit nötig - my $readingsHash = HEOSMaster_PreProcessingReadings($hash,$decode_json) - if( $decode_json->{heos}{command} eq 'system/register_for_change_events' + my $readingsHash = HEOSMaster_PreProcessingReadings( $hash, $decode_json ) + if ( $decode_json->{heos}{command} eq 'system/register_for_change_events' or $decode_json->{heos}{command} eq 'system/check_account' or $decode_json->{heos}{command} eq 'system/sign_in' or $decode_json->{heos}{command} eq 'system/sign_out' ); @@ -1031,470 +1175,547 @@ sub HEOSMaster_WriteReadings($$) { readingsBeginUpdate($hash); ### Event Readings - if( ref($readingsHash) eq "HASH" ) { - - Log3 $name, 4, "HEOSMaster ($name) - response json Hash back from HEOSMaster_PreProcessingReadings"; + if ( ref($readingsHash) eq "HASH" ) { + + Log3 $name, 4, +"HEOSMaster ($name) - response json Hash back from HEOSMaster_PreProcessingReadings"; my $t; my $v; - - while( ( $t, $v ) = each (%{$readingsHash}) ) { - - readingsBulkUpdate( $hash, $t, $v ) if( defined($v) ); + + while ( ( $t, $v ) = each( %{$readingsHash} ) ) { + + readingsBulkUpdate( $hash, $t, $v ) if ( defined($v) ); } } - - readingsBulkUpdate( $hash, "lastCommand", $decode_json->{heos}{command} ); - readingsBulkUpdate( $hash, "lastResult", $decode_json->{heos}{result} ); - if( ref($decode_json->{payload}) ne "ARRAY" ) { - - readingsBulkUpdate( $hash, "lastPlayerId", $decode_json->{payload}{pid} ); - readingsBulkUpdate( $hash, "lastPlayerName", $decode_json->{payload}{name} ); + readingsBulkUpdate( $hash, "lastCommand", $decode_json->{heos}{command} ); + readingsBulkUpdate( $hash, "lastResult", $decode_json->{heos}{result} ); + + if ( ref( $decode_json->{payload} ) ne "ARRAY" ) { + + readingsBulkUpdate( $hash, "lastPlayerId", + $decode_json->{payload}{pid} ); + readingsBulkUpdate( $hash, "lastPlayerName", + $decode_json->{payload}{name} ); } - + readingsEndUpdate( $hash, 1 ); - return undef; + + return; } ################### ### my little Helpers -sub HEOSMaster_ParseMsg($$) { - - my ($hash, $buffer) = @_; - my $name = $hash->{NAME}; - my $open = 0; - my $close = 0; - my $msg = ''; - my $tail = ''; +sub HEOSMaster_ParseMsg { + my ( $hash, $buffer ) = @_; - - if($buffer) { - foreach my $c (split //, $buffer) { - if($open == $close && $open > 0) { + my $name = $hash->{NAME}; + my $open = 0; + my $close = 0; + my $msg = ''; + my $tail = ''; + + if ($buffer) { + foreach my $c ( split //, $buffer ) { + if ( $open == $close && $open > 0 ) { $tail .= $c; - - } elsif(($open == $close) && ($c ne '{')) { - - Log3 $name, 5, "HEOSMaster ($name) - Garbage character before message: " . $c; - - } else { - - if($c eq '{') { - + + } + elsif ( ( $open == $close ) && ( $c ne '{' ) ) { + + Log3 $name, 5, + "HEOSMaster ($name) - Garbage character before message: " + . $c; + + } + else { + + if ( $c eq '{' ) { + $open++; - - } elsif($c eq '}') { - + + } + elsif ( $c eq '}' ) { + $close++; } - + $msg .= $c; } } - - if($open != $close) { - + + if ( $open != $close ) { + $tail = $msg; - $msg = ''; + $msg = ''; } } - return ($msg,$tail); + return ( $msg, $tail ); } -sub HEOSMaster_PreProcessingReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; +sub HEOSMaster_PreProcessingReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; my $reading; my %buffer; - my %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); - + my %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); Log3 $name, 4, "HEOSMaster ($name) - preprocessing readings"; - if ( $decode_json->{heos}{command} eq 'system/register_for_change_events' ) { - - $buffer{'enableChangeEvents'} = $message{enable}; - - } elsif ( $decode_json->{heos}{command} eq 'system/check_account' or $decode_json->{heos}{command} eq 'system/sign_in' ) { - if ( exists $message{signed_out} || exists $message{eid} ) { - - $buffer{'heosAccount'} = "signed_out"; - - } else { - - $buffer{'heosAccount'} = "signed_in as $message{un}"; - HEOSMaster_GetFavorites($hash) if( ReadingsVal($name,"enableChangeEvents", "off") eq "on" ); + if ( $decode_json->{heos}{command} eq 'system/register_for_change_events' ) + { + + $buffer{'enableChangeEvents'} = $message{enable}; + + } + elsif ($decode_json->{heos}{command} eq 'system/check_account' + or $decode_json->{heos}{command} eq 'system/sign_in' ) + { + if ( exists $message{signed_out} || exists $message{eid} ) { + + $buffer{'heosAccount'} = "signed_out"; + } - - } else { - + else { + + $buffer{'heosAccount'} = "signed_in as $message{un}"; + HEOSMaster_GetFavorites($hash) + if ( ReadingsVal( $name, "enableChangeEvents", "off" ) eq "on" ); + } + + } + else { + Log3 $name, 3, "HEOSMaster ($name) - no match found"; return undef; } - + Log3 $name, 4, "HEOSMaster ($name) - Match found for decode_json"; + return \%buffer; } -sub HEOSMaster_FirstRun($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_FirstRun { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSMaster_FirstRun'); - HEOSMaster_Open($hash) if( !IsDisabled($name) ); + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_FirstRun' ); + + return HEOSMaster_Open($hash) if ( !IsDisabled($name) ); } -sub HEOSMaster_GetPlayers($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetPlayers { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetPlayers' ); + HEOSMaster_Write( $hash, 'getPlayers', undef, undef ); - - RemoveInternalTimer($hash,'HEOSMaster_GetPlayers'); - HEOSMaster_Write($hash,'getPlayers',undef,undef); Log3 $name, 4, "HEOSMaster ($name) - getPlayers"; + + return; } -sub HEOSMaster_GetGroups($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetGroups { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetGroups' ); + HEOSMaster_Write( $hash, 'getGroups', undef, undef ); - - RemoveInternalTimer($hash,'HEOSMaster_GetGroups'); - HEOSMaster_Write($hash,'getGroups',undef,undef); Log3 $name, 4, "HEOSMaster ($name) - getGroups"; + + return; } -sub HEOSMaster_EnableChangeEvents($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_EnableChangeEvents { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_EnableChangeEvents' ); + HEOSMaster_Write( $hash, 'enableChangeEvents', 'on', undef ); - - RemoveInternalTimer($hash,'HEOSMaster_EnableChangeEvents'); - HEOSMaster_Write($hash,'enableChangeEvents','on',undef); Log3 $name, 4, "HEOSMaster ($name) - set enableChangeEvents on"; + + retun; } -sub HEOSMaster_GetMusicSources($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetMusicSources { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetMusicSources' ); + HEOSMaster_Write( $hash, 'getMusicSources', undef, undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetMusicSources'); - HEOSMaster_Write($hash,'getMusicSources',undef,undef); Log3 $name, 4, "HEOSMaster ($name) - getMusicSources"; + + return; } -sub HEOSMaster_GetFavorites($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetFavorites { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetFavorites' ); + HEOSMaster_Write( $hash, 'browseSource', 'sid=1028', undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetFavorites'); - HEOSMaster_Write($hash,'browseSource','sid=1028',undef); Log3 $name, 4, "HEOSMaster ($name) - getFavorites"; + + return; } -sub HEOSMaster_GetInputs($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetInputs { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetInputs' ); + HEOSMaster_Write( $hash, 'browseSource', 'sid=1027', undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetInputs'); - HEOSMaster_Write($hash,'browseSource','sid=1027',undef); Log3 $name, 4, "HEOSMaster ($name) - getInputs"; + + return; } -sub HEOSMaster_GetServers($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetServers { + my $hash = shift; + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetServers' ); + HEOSMaster_Write( $hash, 'browseSource', 'sid=1024', undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetServers'); - HEOSMaster_Write($hash,'browseSource','sid=1024',undef); Log3 $name, 4, "HEOSMaster ($name) - getServers"; + + return; } -sub HEOSMaster_GetPlaylists($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetPlaylists { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetPlaylists' ); + HEOSMaster_Write( $hash, 'browseSource', 'sid=1025', undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetPlaylists'); - HEOSMaster_Write($hash,'browseSource','sid=1025',undef); Log3 $name, 4, "HEOSMaster ($name) - getPlaylists"; + + return; } -sub HEOSMaster_GetHistory($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_GetHistory { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_GetHistory' ); + HEOSMaster_Write( $hash, 'browseSource', 'sid=1026', undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_GetHistory'); - HEOSMaster_Write($hash,'browseSource','sid=1026',undef); Log3 $name, 4, "HEOSMaster ($name) - getHistory"; + + return; } -sub HEOSMaster_CheckAccount($) { - - my $hash = shift; - my $name = $hash->{NAME}; +sub HEOSMaster_CheckAccount { + my $hash = shift; + + my $name = $hash->{NAME}; + + RemoveInternalTimer( $hash, 'HEOSMaster_CheckAccount' ); + HEOSMaster_Write( $hash, 'checkAccount', undef, undef ); - - RemoveInternalTimer($hash, 'HEOSMaster_CheckAccount'); - HEOSMaster_Write($hash,'checkAccount',undef,undef); Log3 $name, 4, "HEOSMaster ($name) - checkAccount"; + + return; } -sub HEOSMaster_StorePassword($@) { - - my ($hash, $name, $password) = @_; - my $index = $hash->{TYPE}."_".$name."_passwd"; - my $key = getUniqueId().$index; +sub HEOSMaster_StorePassword { + my ( $hash, $name, $password ) = @_; + + my $index = $hash->{TYPE} . "_" . $name . "_passwd"; + my $key = getUniqueId() . $index; my $enc_pwd = ""; - - if(eval "use Digest::MD5;1") { - - $key = Digest::MD5::md5_hex(unpack "H*", $key); + if ( eval "use Digest::MD5;1" ) { + + $key = Digest::MD5::md5_hex( unpack "H*", $key ); $key .= Digest::MD5::md5_hex($key); } - - for my $char (split //, $password) { - - my $encode=chop($key); - $enc_pwd.=sprintf("%.2x",ord($char)^ord($encode)); - $key=$encode.$key; + + for my $char ( split //, $password ) { + + my $encode = chop($key); + $enc_pwd .= sprintf( "%.2x", ord($char) ^ ord($encode) ); + $key = $encode . $key; } - - my $err = setKeyValue($index, $enc_pwd); - return "error while saving the password - $err" if(defined($err)); + + my $err = setKeyValue( $index, $enc_pwd ); + return "error while saving the password - $err" if ( defined($err) ); return "password successfully saved"; } -sub HEOSMaster_Rename(@) { - +sub HEOSMaster_Rename { my ( $new, $old ) = @_; + my $hash = $defs{$new}; - - StorePassword( $hash, $new, ReadPassword($hash,$old) ); + + StorePassword( $hash, $new, ReadPassword( $hash, $old ) ); setKeyValue( $hash->{TYPE} . "_" . $old . "_passwd", undef ); - return undef; + return; } -sub HEOSMaster_ReadPassword($$) { - - my ($hash, $name) = @_; - my $index = $hash->{TYPE}."_".$name."_passwd"; - my $key = getUniqueId().$index; - my ($password, $err); +sub HEOSMaster_ReadPassword { + my ( $hash, $name ) = @_; + + my $index = $hash->{TYPE} . "_" . $name . "_passwd"; + my $key = getUniqueId() . $index; + my ( $password, $err ); - Log3 $name, 4, "HEOSMaster ($name) - Read password from file"; - - ($err, $password) = getKeyValue($index); + + ( $err, $password ) = getKeyValue($index); if ( defined($err) ) { - - Log3 $name, 4, "HEOSMaster ($name) - unable to read password from file: $err"; - return undef; - + + Log3 $name, 4, + "HEOSMaster ($name) - unable to read password from file: $err"; + + return; + } - + if ( defined($password) ) { if ( eval "use Digest::MD5;1" ) { - - $key = Digest::MD5::md5_hex(unpack "H*", $key); + + $key = Digest::MD5::md5_hex( unpack "H*", $key ); $key .= Digest::MD5::md5_hex($key); } - + my $dec_pwd = ''; - - for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g)) { - - my $decode=chop($key); - $dec_pwd.=chr(ord($char)^ord($decode)); - $key=$decode.$key; + + for my $char ( map { pack( 'C', hex($_) ) } ( $password =~ /(..)/g ) ) { + + my $decode = chop($key); + $dec_pwd .= chr( ord($char) ^ ord($decode) ); + $key = $decode . $key; } - + return $dec_pwd; - - } else { - + + } + else { + Log3 $name, 4, "HEOSMaster ($name) - No password in file"; - return undef; + return; } + + return; } -sub HEOSMaster_MakePlayLink3($$$$) { +sub HEOSMaster_MakePlayLink3 { + my ( $name, $message, $item, $idx ) = @_; - my ($name, $message, $item, $idx) = @_; my $xcmd; my $xtext = $message->{sid}; + if ( ( exists $item->{playable} && $item->{playable} eq "yes" ) + || exists $item->{qid} ) + { + + $xcmd = + 'cmd' . uri_escape( '=set ' . $name . ' input ' . $message->{sid} ); - if ( (exists $item->{playable} && $item->{playable} eq "yes") || exists $item->{qid} ) { - - $xcmd = 'cmd'.uri_escape('=set '.$name.' input '.$message->{sid}); - - } else { - - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); } - + else { + + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); + } + if ( defined $item->{sid} ) { - - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); - $xcmd .= uri_escape(",".$item->{sid}); - $xtext .= ','.$item->{sid}; - - } elsif ( defined $item->{cid} ) { - if ( $item->{type} eq "album" ) { - - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); - } - - $xcmd .= uri_escape(",".$item->{cid}); - $xtext .= ','.$item->{cid}; - - } elsif ( defined $item->{mid} ) { - if ( $message->{sid} eq "1028" ) { - - $xcmd .= ','.$idx; - $xtext .= ','.$idx; - - } elsif ( defined $message->{cid} ) { - - $xcmd .= uri_escape(','.$message->{cid}.','.$item->{mid}); - $xtext .= ','.$message->{cid}.','.$item->{mid}; - - } else { - - $xcmd = 'cmd'.uri_escape('=set '.$name.' input 1027'); - $xcmd .= uri_escape(','.$message->{sid}.','.$item->{mid}); - $xtext = '1027,'.$message->{sid}.','.$item->{mid}; - } - } elsif ( defined $item->{qid} ) { - - $xcmd .= ','.$item->{qid}; - $xtext .= ','.$item->{qid}; + + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); + $xcmd .= uri_escape( "," . $item->{sid} ); + $xtext .= ',' . $item->{sid}; + } - + elsif ( defined $item->{cid} ) { + if ( $item->{type} eq "album" ) { + + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); + } + + $xcmd .= uri_escape( "," . $item->{cid} ); + $xtext .= ',' . $item->{cid}; + + } + elsif ( defined $item->{mid} ) { + if ( $message->{sid} eq "1028" ) { + + $xcmd .= ',' . $idx; + $xtext .= ',' . $idx; + + } + elsif ( defined $message->{cid} ) { + + $xcmd .= uri_escape( ',' . $message->{cid} . ',' . $item->{mid} ); + $xtext .= ',' . $message->{cid} . ',' . $item->{mid}; + + } + else { + + $xcmd = 'cmd' . uri_escape( '=set ' . $name . ' input 1027' ); + $xcmd .= uri_escape( ',' . $message->{sid} . ',' . $item->{mid} ); + $xtext = '1027,' . $message->{sid} . ',' . $item->{mid}; + } + } + elsif ( defined $item->{qid} ) { + + $xcmd .= ',' . $item->{qid}; + $xtext .= ',' . $item->{qid}; + } + $xcmd = "FW_cmd('$FW_ME$FW_subdir?XHR=1&$xcmd')"; - return '
  • '.sprintf( "%-35s %-15s %s", $xtext, $item->{type}, $item->{name} )."
  • \n"; + return +'
  • ' + . sprintf( "%-35s %-15s %s", $xtext, $item->{type}, $item->{name} ) + . "
  • \n"; + + return; } -sub HEOSMaster_MakePlayLink($$$$$$$) { +sub HEOSMaster_MakePlayLink { + my ( $type, $name, $message, $item, $idx, $xsize, $ysize ) = @_; - my ($type, $name, $message, $item, $idx, $xsize, $ysize) = @_; my $xcmd; my $xtext = $message->{sid}; - $ysize = '10.75em' if (!defined($ysize)); + $ysize = '10.75em' if ( !defined($ysize) ); - if ( (exists $item->{playable} && $item->{playable} eq "yes") || exists $item->{qid} ) { + if ( ( exists $item->{playable} && $item->{playable} eq "yes" ) + || exists $item->{qid} ) + { - $xcmd = 'cmd'.uri_escape('=set '.$name.' input '.$message->{sid}); - $xtext = '*'.$xtext; + $xcmd = + 'cmd' . uri_escape( '=set ' . $name . ' input ' . $message->{sid} ); + $xtext = '*' . $xtext; - } else { + } + else { - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); } if ( defined $item->{sid} ) { - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); - $xcmd .= uri_escape(",".$item->{sid}); - $xtext .= ','.$item->{sid}; + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); + $xcmd .= uri_escape( "," . $item->{sid} ); + $xtext .= ',' . $item->{sid}; - } elsif ( defined $item->{cid} ) { + } + elsif ( defined $item->{cid} ) { if ( $item->{type} eq "album" ) { - $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$message->{sid}); + $xcmd = + 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $message->{sid} ); } - $xcmd .= uri_escape(",".$item->{cid}); - $xtext .= ','.$item->{cid}; + $xcmd .= uri_escape( "," . $item->{cid} ); + $xtext .= ',' . $item->{cid}; - } elsif ( defined $item->{mid} ) { + } + elsif ( defined $item->{mid} ) { if ( $message->{sid} eq "1028" ) { - $xcmd .= ','.$idx; - $xtext .= ','.$idx; + $xcmd .= ',' . $idx; + $xtext .= ',' . $idx; - } elsif ( defined $message->{cid} ) { - - $xcmd .= uri_escape(','.$message->{cid}.','.$item->{mid}); - $xtext .= ','.$message->{cid}.','.$item->{mid}; - - } else { - - $xcmd = 'cmd'.uri_escape('=set '.$name.' input 1027'); - $xcmd .= uri_escape(','.$message->{sid}.','.$item->{mid}); - $xtext = '1027,'.$message->{sid}.','.$item->{mid}; } - } elsif ( defined $item->{qid} ) { + elsif ( defined $message->{cid} ) { - $xcmd .= ','.$item->{qid}; - $xtext .= ','.$item->{qid}; + $xcmd .= uri_escape( ',' . $message->{cid} . ',' . $item->{mid} ); + $xtext .= ',' . $message->{cid} . ',' . $item->{mid}; + + } + else { + + $xcmd = 'cmd' . uri_escape( '=set ' . $name . ' input 1027' ); + $xcmd .= uri_escape( ',' . $message->{sid} . ',' . $item->{mid} ); + $xtext = '1027,' . $message->{sid} . ',' . $item->{mid}; + } + } + elsif ( defined $item->{qid} ) { + + $xcmd .= ',' . $item->{qid}; + $xtext .= ',' . $item->{qid}; } - if( $type eq 'FHEMWEB' ) { - + if ( $type eq 'FHEMWEB' ) { + $xcmd = "FW_cmd('$FW_ME$FW_subdir?XHR=1&$xcmd')"; if ( defined $item->{image_url} && $item->{image_url} ne "" ) { - return '
    '.$item->{name}.'
    '; + return + '
    ' + . $item->{name} + . '
    '; - #return '
    '.$item->{name}."
    \n\n"; - } else { - return '

    '.$item->{name}.'

    '; - #return '\n\n"; +#return '
    '.$item->{name}."
    \n\n"; + } + else { + return + '

    ' + . $item->{name} + . '

    '; + +#return '\n\n"; } - } else { + } + else { return sprintf( "%-15s %s\n", $xtext, $item->{name} ); } + + return; } -sub HEOSMaster_MakeImage($$) { - - my ($url, $size) = @_; +sub HEOSMaster_MakeImage { + my ( $url, $size ) = @_; my $ret .= "\n"; return $ret; } - - - - - - - 1; - - - =pod =item device =item summary Modul to controls the Denon multiroom soundsystem diff --git a/21_HEOSPlayer.pm b/21_HEOSPlayer.pm index 789c66f..773e258 100644 --- a/21_HEOSPlayer.pm +++ b/21_HEOSPlayer.pm @@ -37,286 +37,353 @@ use warnings; use JSON qw(decode_json); use Encode qw(encode_utf8); use URI::Escape; + #use Data::Dumper; my $version = "1.0.5"; +sub HEOSPlayer_Initialize { + my ($hash) = shift; + $hash->{Match} = +'.*{"command":."player.*|.*{"command":."event/player.*|.*{"command":."event\/repeat_mode_changed.*|.*{"command":."event\/shuffle_mode_changed.*|.*{"command":."event\/favorites_changed.*'; - -# Declare functions -sub HEOSPlayer_Initialize($); -sub HEOSPlayer_Define($$); -sub HEOSPlayer_Undef($$); -sub HEOSPlayer_Attr(@); -sub HEOSPlayer_Parse($$); -sub HEOSPlayer_WriteReadings($$); -sub HEOSPlayer_Set($$@); -sub HEOSPlayer_PreProcessingReadings($$); -sub HEOSPlayer_GetPlayerInfo($); -sub HEOSPlayer_GetPlayState($); -sub HEOSPlayer_GetQueue($); -sub HEOSPlayer_GetNowPlayingMedia($); -sub HEOSPlayer_GetPlayMode($); -sub HEOSPlayer_GetVolume($); -sub HEOSPlayer_Get($$@); -sub HEOSPlayer_GetMute($); -sub HEOSPlayer_MakePlayLink($$$$$$$$); - - - -sub HEOSPlayer_Initialize($) { - - my ($hash) = @_; - - $hash->{Match} = '.*{"command":."player.*|.*{"command":."event/player.*|.*{"command":."event\/repeat_mode_changed.*|.*{"command":."event\/shuffle_mode_changed.*|.*{"command":."event\/favorites_changed.*'; - - # Provider - $hash->{SetFn} = "HEOSPlayer_Set"; - $hash->{GetFn} = "HEOSPlayer_Get"; - $hash->{DefFn} = "HEOSPlayer_Define"; - $hash->{UndefFn} = "HEOSPlayer_Undef"; - $hash->{AttrFn} = "HEOSPlayer_Attr"; - $hash->{ParseFn} = "HEOSPlayer_Parse"; - $hash->{AttrList} = "IODev ". - "disable:1 ". - "mute2play:1 ". - "channelring:1 ". - $readingFnAttributes; + $hash->{SetFn} = "HEOSPlayer_Set"; + $hash->{GetFn} = "HEOSPlayer_Get"; + $hash->{DefFn} = "HEOSPlayer_Define"; + $hash->{UndefFn} = "HEOSPlayer_Undef"; + $hash->{AttrFn} = "HEOSPlayer_Attr"; + $hash->{ParseFn} = "HEOSPlayer_Parse"; + $hash->{AttrList} = + "IODev " + . "disable:1 " + . "mute2play:1 " + . "channelring:1 " + . $readingFnAttributes; + + foreach my $d ( sort keys %{ $modules{HEOSPlayer}{defptr} } ) { - foreach my $d(sort keys %{$modules{HEOSPlayer}{defptr}}) { - my $hash = $modules{HEOSPlayer}{defptr}{$d}; - $hash->{VERSION} = $version; + $hash->{VERSION} = $version; } } -sub HEOSPlayer_Define($$) { - +sub HEOSPlayer_Define { my ( $hash, $def ) = @_; + my @a = split( "[ \t]+", $def ); splice( @a, 1, 1 ); + my $iodev; my $i = 0; - - foreach my $param ( @a ) { - if( $param =~ m/IODev=([^\s]*)/ ) { - + foreach my $param (@a) { + if ( $param =~ m/IODev=([^\s]*)/ ) { + $iodev = $1; splice( @a, $i, 3 ); last; } - + $i++; } - - return "too few parameters: define HEOSPlayer " if( @a < 2 ); - - my ($name,$pid) = @a; - $hash->{PID} = $pid; - $hash->{VERSION} = $version; - AssignIoPort($hash,$iodev) if( !$hash->{IODev} ); - - if(defined($hash->{IODev}->{NAME})) { - - Log3 $name, 3, "HEOSPlayer ($name) - I/O device is " . $hash->{IODev}->{NAME}; - - } else { - + return "too few parameters: define HEOSPlayer " if ( @a < 2 ); + + my ( $name, $pid ) = @a; + + $hash->{PID} = $pid; + $hash->{VERSION} = $version; + AssignIoPort( $hash, $iodev ) if ( !$hash->{IODev} ); + + if ( defined( $hash->{IODev}->{NAME} ) ) { + + Log3 $name, 3, + "HEOSPlayer ($name) - I/O device is " . $hash->{IODev}->{NAME}; + + } + else { + Log3 $name, 1, "HEOSPlayer ($name) - no I/O device"; } - + $iodev = $hash->{IODev}->{NAME}; my $code = abs($pid); - - $code = $iodev."-".$code if( defined($iodev) ); + + $code = $iodev . "-" . $code if ( defined($iodev) ); my $d = $modules{HEOSPlayer}{defptr}{$code}; - - return "HEOSPlayer device $hash->{pid} on HEOSMaster $iodev already defined as $d->{NAME}." - if( defined($d) && $d->{IODev} == $hash->{IODev} && $d->{NAME} ne $name ); + + return +"HEOSPlayer device $hash->{pid} on HEOSMaster $iodev already defined as $d->{NAME}." + if ( defined($d) + && $d->{IODev} == $hash->{IODev} + && $d->{NAME} ne $name ); Log3 $name, 3, "HEOSPlayer ($name) - defined with Code: $code"; - $attr{$name}{room} = "HEOS" if( !defined( $attr{$name}{room} ) ); - $attr{$name}{devStateIcon} = "on:10px-kreis-gruen off:10px-kreis-rot" if( !defined( $attr{$name}{devStateIcon} ) ); - - if( $init_done ) { - - InternalTimer( gettimeofday()+int(rand(2)), "HEOSPlayer_GetPlayerInfo", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(4)), "HEOSPlayer_GetPlayState", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(6)), "HEOSPlayer_GetNowPlayingMedia", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(8)), "HEOSPlayer_GetPlayMode", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(10)), "HEOSPlayer_GetVolume", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(12)), "HEOSPlayer_GetMute", $hash, 0 ); - InternalTimer( gettimeofday()+int(rand(14)), "HEOSPlayer_GetQueue", $hash, 0 ); - - } else { - - InternalTimer( gettimeofday()+15+int(rand(2)), "HEOSPlayer_GetPlayerInfo", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(4)), "HEOSPlayer_GetPlayState", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(6)), "HEOSPlayer_GetNowPlayingMedia", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(8)), "HEOSPlayer_GetPlayMode", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(10)), "HEOSPlayer_GetVolume", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(12)), "HEOSPlayer_GetMute", $hash, 0 ); - InternalTimer( gettimeofday()+15+int(rand(14)), "HEOSPlayer_GetQueue", $hash, 0 ); + $attr{$name}{room} = "HEOS" if ( !defined( $attr{$name}{room} ) ); + $attr{$name}{devStateIcon} = "on:10px-kreis-gruen off:10px-kreis-rot" + if ( !defined( $attr{$name}{devStateIcon} ) ); + + if ($init_done) { + + InternalTimer( gettimeofday() + int( rand(2) ), + "HEOSPlayer_GetPlayerInfo", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(4) ), + "HEOSPlayer_GetPlayState", $hash, 0 ); + InternalTimer( + gettimeofday() + int( rand(6) ), + "HEOSPlayer_GetNowPlayingMedia", + $hash, 0 + ); + InternalTimer( gettimeofday() + int( rand(8) ), + "HEOSPlayer_GetPlayMode", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(10) ), + "HEOSPlayer_GetVolume", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(12) ), + "HEOSPlayer_GetMute", $hash, 0 ); + InternalTimer( gettimeofday() + int( rand(14) ), + "HEOSPlayer_GetQueue", $hash, 0 ); + } - + else { + + InternalTimer( gettimeofday() + 15 + int( rand(2) ), + "HEOSPlayer_GetPlayerInfo", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(4) ), + "HEOSPlayer_GetPlayState", $hash, 0 ); + InternalTimer( + gettimeofday() + 15 + int( rand(6) ), + "HEOSPlayer_GetNowPlayingMedia", + $hash, 0 + ); + InternalTimer( gettimeofday() + 15 + int( rand(8) ), + "HEOSPlayer_GetPlayMode", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(10) ), + "HEOSPlayer_GetVolume", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(12) ), + "HEOSPlayer_GetMute", $hash, 0 ); + InternalTimer( gettimeofday() + 15 + int( rand(14) ), + "HEOSPlayer_GetQueue", $hash, 0 ); + } + readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'state','Initialized'); - readingsBulkUpdate($hash, 'volumeUp', 5); - readingsBulkUpdate($hash, 'volumeDown', 5); - readingsEndUpdate($hash, 1); - + readingsBulkUpdate( $hash, 'state', 'Initialized' ); + readingsBulkUpdate( $hash, 'volumeUp', 5 ); + readingsBulkUpdate( $hash, 'volumeDown', 5 ); + readingsEndUpdate( $hash, 1 ); + $modules{HEOSPlayer}{defptr}{$code} = $hash; - return undef; + + return; } -sub HEOSPlayer_Undef($$) { - +sub HEOSPlayer_Undef { my ( $hash, $arg ) = @_; - my $pid = $hash->{PID}; + + my $pid = $hash->{PID}; my $name = $hash->{NAME}; - RemoveInternalTimer($hash); my $code = abs($pid); - $code = $hash->{IODev}->{NAME} ."-". $code if( defined($hash->{IODev}->{NAME}) ); - delete($modules{HEOSPlayer}{defptr}{$code}); - + $code = $hash->{IODev}->{NAME} . "-" . $code + if ( defined( $hash->{IODev}->{NAME} ) ); + delete( $modules{HEOSPlayer}{defptr}{$code} ); + Log3 $name, 3, "HEOSPlayer ($name) - device $name deleted with Code: $code"; - return undef; + + return; } -sub HEOSPlayer_Attr(@) { - +sub HEOSPlayer_Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; - my $hash = $defs{$name}; + + my $hash = $defs{$name}; my $token = $hash->{IODev}->{TOKEN}; - - if( $attrName eq "disable" ) { - if( $cmd eq "set" and $attrVal eq "1" ) { - - readingsSingleUpdate ( $hash, "state", "disabled", 1 ); + if ( $attrName eq "disable" ) { + if ( $cmd eq "set" and $attrVal eq "1" ) { + + readingsSingleUpdate( $hash, "state", "disabled", 1 ); Log3 $name, 3, "HEOSPlayer ($name) - disabled"; - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSPlayer ($name) - enabled"; } } - - if( $attrName eq "disabledForIntervals" ) { - if( $cmd eq "set" ) { - + + if ( $attrName eq "disabledForIntervals" ) { + if ( $cmd eq "set" ) { + Log3 $name, 3, "HEOSPlayer ($name) - enable disabledForIntervals"; - readingsSingleUpdate ( $hash, "state", "Unknown", 1 ); - - } elsif( $cmd eq "del" ) { - - readingsSingleUpdate ( $hash, "state", "active", 1 ); + readingsSingleUpdate( $hash, "state", "Unknown", 1 ); + + } + elsif ( $cmd eq "del" ) { + + readingsSingleUpdate( $hash, "state", "active", 1 ); Log3 $name, 3, "HEOSPlayer ($name) - delete disabledForIntervals"; } } + + return; } -sub HEOSPlayer_Get($$@) { +sub HEOSPlayer_Get { + my ( $hash, $name, @aa ) = @_; + my ( $cmd, @args ) = @aa; - my ($hash, $name, @aa) = @_; - my ($cmd, @args) = @aa; - my $pid = $hash->{PID}; - my $result = ""; - my $me = {}; + my $pid = $hash->{PID}; + my $result = ""; + my $me = {}; my $ret; - - $me->{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' ); + + $me->{cl} = $hash->{CL} if ( ref( $hash->{CL} ) eq 'HASH' ); $me->{name} = $hash->{NAME}; $me->{pid} = $hash->{PID}; - #Leerzeichen müßen für die Rückgabe escaped werden sonst werden sie falsch angezeigt - if( $cmd eq 'channelscount' ) { - - #gibt die Favoritenanzahl zurück - return scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} ); - - } elsif( $cmd eq 'ls' ) { +#Leerzeichen müßen für die Rückgabe escaped werden sonst werden sie falsch angezeigt + if ( $cmd eq 'channelscount' ) { - my $param = shift( @args ); - $param = '' if( !$param ); + #gibt die Favoritenanzahl zurück + return scalar( @{ $hash->{IODev}{helper}{favorites} } ) + if ( defined $hash->{IODev}{helper}{favorites} ); + + } + elsif ( $cmd eq 'ls' ) { + + my $param = shift(@args); + $param = '' if ( !$param ); if ( $param eq '' ) { - if( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { + if ( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { $ret = '
    '; $ret .= '

    Musik


    '; $ret .= '
    '; - } else { + } + else { $ret = "Musik\n"; $ret .= sprintf( "%-15s %s\n", 'key', 'title' ); } - foreach my $item (@{ $hash->{IODev}{helper}{sources}}) { - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, $item->{sid}, $item->{type}, $item->{name}, $item->{image_url}, 128, 50); + foreach my $item ( @{ $hash->{IODev}{helper}{sources} } ) { + $ret .= HEOSPlayer_MakePlayLink( $me->{cl}->{TYPE}, + $hash->{NAME}, $item->{sid}, $item->{type}, $item->{name}, + $item->{image_url}, 128, 50 ); } - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, "1025", "heos_service", "Playlist", "https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png", 128, 32); + $ret .= HEOSPlayer_MakePlayLink( + $me->{cl}->{TYPE}, + $hash->{NAME}, + "1025", + "heos_service", + "Playlist", +"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png", + 128, + 32 + ); - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, "1026", "heos_service", "Verlauf", "https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_history.png", 128, 32); + $ret .= HEOSPlayer_MakePlayLink( + $me->{cl}->{TYPE}, + $hash->{NAME}, + "1026", + "heos_service", + "Verlauf", +"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_history.png", + 128, + 32 + ); - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, "1027", "heos_service", "Eingänge", "https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_aux.png", 128, 32); + $ret .= HEOSPlayer_MakePlayLink( + $me->{cl}->{TYPE}, + $hash->{NAME}, + "1027", + "heos_service", + "Eingänge", +"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_aux.png", + 128, + 32 + ); - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, "1028", "heos_service", "Favoriten", "https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_favorites.png", 128, 32); + $ret .= HEOSPlayer_MakePlayLink( + $me->{cl}->{TYPE}, + $hash->{NAME}, + "1028", + "heos_service", + "Favoriten", +"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_favorites.png", + 128, + 32 + ); - $ret .= HEOSPlayer_MakePlayLink($me->{cl}->{TYPE}, $hash->{NAME}, "1029", "heos_service", "Warteschlange", "https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png", 128, 32); + $ret .= HEOSPlayer_MakePlayLink( + $me->{cl}->{TYPE}, + $hash->{NAME}, + "1029", + "heos_service", + "Warteschlange", +"https://production.ws.skyegloup.com:443/media/images/service/logos/musicsource_logo_playlists.png", + 128, + 32 + ); - - if( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { + if ( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { $ret .= '
    '; $ret =~ s/&/&/g; $ret =~ s/'/'/g; $ret =~ s/\n/
    /g; - $ret = "
    $ret
    " if( $ret =~ m/ / ); + $ret = "
    $ret
    " if ( $ret =~ m/ / ); $ret = "$ret"; } - + return $ret; - } else { + } + else { - my ($sid,$cid) = split /,/,$param; + my ( $sid, $cid ) = split /,/, $param; if ( $sid eq "1025" ) { - + $me->{sourcename} = "Playlist"; - - } elsif ( $sid eq "1026" ) { - + + } + elsif ( $sid eq "1026" ) { + $me->{sourcename} = "Verlauf"; - - } elsif ( $sid eq "1027" ) { - + + } + elsif ( $sid eq "1027" ) { + $me->{sourcename} = "Eingänge"; - - } elsif ( $sid eq "1028" ) { - + + } + elsif ( $sid eq "1028" ) { + $me->{sourcename} = "Favoriten"; - - } elsif ( $sid eq "1029" ) { - + + } + elsif ( $sid eq "1029" ) { + $me->{sourcename} = "Warteschlange"; - - } else { - - my @sids = map { $_->{name} } grep { $_->{sid} =~ /$sid/i } (@{ $hash->{IODev}{helper}{sources} }); - $me->{sourcename} = $sids[0] if ( scalar @sids > 0); + + } + else { + + my @sids = + map { $_->{name} } + grep { $_->{sid} =~ /$sid/i } + ( @{ $hash->{IODev}{helper}{sources} } ); + $me->{sourcename} = $sids[0] if ( scalar @sids > 0 ); } my $heosCmd = "browseSource"; @@ -324,35 +391,51 @@ sub HEOSPlayer_Get($$@) { if ( defined $sid && defined $cid && $cid ne "" ) { if ( $sid eq "1027" ) { - + $action = "sid=$cid"; - } elsif ( $sid eq "1026" ) { - + } + elsif ( $sid eq "1026" ) { + $me->{sourcename} .= "/$cid"; $action = "sid=$sid&cid=$cid"; - - } else { - - my @cids = map { $_->{name} } grep { $_->{cid} =~ /\Q$cid\E/i } (@{ $hash->{IODev}{helper}{media} }); - $me->{sourcename} .= "/".$cids[0] if ( scalar @cids > 0); - $action = "sid=$sid&cid=$cid"; - + } - - } elsif ( defined $sid && $sid eq "1029" ) { + else { + + my @cids = + map { $_->{name} } + grep { $_->{cid} =~ /\Q$cid\E/i } + ( @{ $hash->{IODev}{helper}{media} } ); + $me->{sourcename} .= "/" . $cids[0] if ( scalar @cids > 0 ); + $action = "sid=$sid&cid=$cid"; + + } + + } + elsif ( defined $sid && $sid eq "1029" ) { + + if ( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { - if( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { - $ret = '
    '; - $ret .= '

    '.$hash->{NAME}.'

    '; - $ret .= '

    Warteschlange


    '; + $ret .= '

    ' + . $hash->{NAME} . '

    '; + $ret .= + '

    Warteschlange


    '; - $ret .= ''; + $ret .= +''; $ret .= '
    '; - } else { + } + else { $ret .= "Warteschlange von $hash->{NAME} \n"; $ret .= sprintf( "%-15s %s\n", 'key', 'title' ); @@ -362,42 +445,60 @@ sub HEOSPlayer_Get($$@) { my $itemtext; my $itemkey; - foreach my $item (@{ $hash->{helper}{queue}}) { + foreach my $item ( @{ $hash->{helper}{queue} } ) { $itemtext = $item->{artist}; - $itemtext .= ( defined $item->{artist} ) ? (", ".$item->{album}) : ($item->{album}) if ( defined $item->{album} ); - $itemkey = '1029,'.++$x; + $itemtext .= + ( defined $item->{artist} ) + ? ( ", " . $item->{album} ) + : ( $item->{album} ) + if ( defined $item->{album} ); + $itemkey = '1029,' . ++$x; - if( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { - my $xcmd = 'cmd'.uri_escape('=set '.$hash->{NAME}.' input '.$itemkey); + if ( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { + my $xcmd = 'cmd' + . uri_escape( + '=set ' . $hash->{NAME} . ' input ' . $itemkey ); $xcmd = "FW_cmd('$FW_ME$FW_subdir?XHR=1&$xcmd')"; - $ret .= '

    '.$item->{song}.'

    '.$itemtext."

    "; - #$ret .= HEOSPlayer_MakePlayLink($hash->{NAME}, "1029,".++$x, "heos_queue", $item->{song}, $item->{image_url}, 64, 64); - } else { - $ret .= sprintf( "%-15s %s\n", $itemkey, $item->{song}.", ".$itemtext ) + $ret .= + '

    ' + . $item->{song} + . '

    ' + . $itemtext + . "

    "; + +#$ret .= HEOSPlayer_MakePlayLink($hash->{NAME}, "1029,".++$x, "heos_queue", $item->{song}, $item->{image_url}, 64, 64); + } + else { + $ret .= sprintf( "%-15s %s\n", + $itemkey, $item->{song} . ", " . $itemtext ); } } - if( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { + if ( $me->{cl}->{TYPE} eq 'FHEMWEB' ) { $ret .= '
    '; $ret =~ s/&/&/g; $ret =~ s/'/'/g; $ret =~ s/\n/
    /g; - $ret = "
    $ret
    " if( $ret =~ m/ / ); + $ret = "
    $ret
    " if ( $ret =~ m/ / ); $ret = "$ret"; } - - asyncOutput( $me->{cl}, $ret ); - - } else { - + + asyncOutput( $me->{cl}, $ret ); + + } + else { + $action = "sid=$sid"; } - IOWrite($hash,$heosCmd,$action,$me); - Log3 $name, 4, "HEOSPlayer ($name) - IOWrite: $heosCmd $action IODevHash=$hash->{IODev}"; - + IOWrite( $hash, $heosCmd, $action, $me ); + Log3 $name, 4, +"HEOSPlayer ($name) - IOWrite: $heosCmd $action IODevHash=$hash->{IODev}"; + return undef; } } @@ -407,678 +508,828 @@ sub HEOSPlayer_Get($$@) { return "Unknown argument $cmd, choose one of $list"; } -sub HEOSPlayer_Set($$@) { +sub HEOSPlayer_Set { + my ( $hash, $name, @aa ) = @_; + my ( $cmd, @args ) = @aa; - my ($hash, $name, @aa) = @_; - my ($cmd, @args) = @aa; - my $pid = $hash->{PID}; + my $pid = $hash->{PID}; my $action; my $heosCmd; my $rvalue; my $favoritcount = 1; - my $qcount = 1; - my $string = "pid=$pid"; + my $qcount = 1; + my $string = "pid=$pid"; - return undef unless ( ReadingsVal($name, "state", "off") eq "on" ); + return undef unless ( ReadingsVal( $name, "state", "off" ) eq "on" ); - if( $cmd eq 'getPlayerInfo' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - - } elsif( $cmd eq 'getPlayState' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - - } elsif( $cmd eq 'getPlayMode' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - - } elsif( $cmd eq 'getNowPlayingMedia' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = $cmd; - - } elsif( $cmd eq 'repeat' ) { + if ( $cmd eq 'getPlayerInfo' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + + } + elsif ( $cmd eq 'getPlayState' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + + } + elsif ( $cmd eq 'getPlayMode' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + + } + elsif ( $cmd eq 'getNowPlayingMedia' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = $cmd; + + } + elsif ( $cmd eq 'repeat' ) { my $param = "one|all|off"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - $heosCmd = 'setPlayMode'; - $rvalue = 'on_'.$args[0]; - $rvalue = 'off' if($rvalue eq 'on_off'); - $action = "repeat=$rvalue&shuffle=".ReadingsVal($name,'shuffle','off'); - - } elsif( $cmd eq 'shuffle' ) { + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + $heosCmd = 'setPlayMode'; + $rvalue = 'on_' . $args[0]; + $rvalue = 'off' if ( $rvalue eq 'on_off' ); + $action = + "repeat=$rvalue&shuffle=" . ReadingsVal( $name, 'shuffle', 'off' ); + + } + elsif ( $cmd eq 'shuffle' ) { my $param = "on|off"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - $heosCmd = 'setPlayMode'; - $rvalue = 'on_'.ReadingsVal($name,'repeat','off'); - $rvalue = 'off' if($rvalue eq 'on_off'); - $action = "repeat=$rvalue&shuffle=$args[0]"; - - } elsif( $cmd eq 'play' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - - } elsif( $cmd eq 'stop' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - - } elsif( $cmd eq 'pause' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'setPlayState'; - $action = "state=$cmd"; - - } elsif( $cmd eq 'mute' ) { + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + $heosCmd = 'setPlayMode'; + $rvalue = 'on_' . ReadingsVal( $name, 'repeat', 'off' ); + $rvalue = 'off' if ( $rvalue eq 'on_off' ); + $action = "repeat=$rvalue&shuffle=$args[0]"; + + } + elsif ( $cmd eq 'play' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + + } + elsif ( $cmd eq 'stop' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + + } + elsif ( $cmd eq 'pause' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'setPlayState'; + $action = "state=$cmd"; + + } + elsif ( $cmd eq 'mute' ) { my $param = "on|off"; - return "usage: $cmd $param" if( @args != 1 || ! grep { $_ =~ /$args[0]/ } split(/\|/, $param) ); - - $heosCmd = 'setMute'; - $action = "state=$args[0]"; - - } elsif( $cmd eq 'volume' ) { - return "usage: $cmd 0-100" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 100 || $args[0] < 0); - - $heosCmd = 'setVolume'; - $action = "level=$args[0]"; - - } elsif( $cmd eq 'volumeUp' ) { - return "usage: $cmd 0-10" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 10 || $args[0] < 1); - - $heosCmd = $cmd; - $action = "step=$args[0]"; - - } elsif( $cmd eq 'volumeDown' ) { - return "usage: $cmd 0-10" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > 10 || $args[0] < 1); - - $heosCmd = $cmd; - $action = "step=$args[0]"; - - } elsif( $cmd eq 'groupWithMember' ) { - return "usage: $cmd" if( @args != 1 ); - - foreach ( split('\,', $args[0]) ) { - - $string .= ",$defs{$_}->{PID}" if ( defined $defs{$_} ); + return "usage: $cmd $param" + if ( @args != 1 || !grep { $_ =~ /$args[0]/ } split( /\|/, $param ) ); + + $heosCmd = 'setMute'; + $action = "state=$args[0]"; + + } + elsif ( $cmd eq 'volume' ) { + return "usage: $cmd 0-100" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 100 + || $args[0] < 0 ); + + $heosCmd = 'setVolume'; + $action = "level=$args[0]"; + + } + elsif ( $cmd eq 'volumeUp' ) { + return "usage: $cmd 0-10" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 10 + || $args[0] < 1 ); + + $heosCmd = $cmd; + $action = "step=$args[0]"; + + } + elsif ( $cmd eq 'volumeDown' ) { + return "usage: $cmd 0-10" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > 10 + || $args[0] < 1 ); + + $heosCmd = $cmd; + $action = "step=$args[0]"; + + } + elsif ( $cmd eq 'groupWithMember' ) { + return "usage: $cmd" if ( @args != 1 ); + + foreach ( split( '\,', $args[0] ) ) { + + $string .= ",$defs{$_}->{PID}" if ( defined $defs{$_} ); printf "String: $string\n"; } - - $heosCmd = 'createGroup'; - - } elsif( $cmd eq 'groupClear' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'createGroup'; - - } elsif( $cmd eq 'next' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'playNext'; - - } elsif( $cmd eq 'prev' ) { - return "usage: $cmd" if( @args != 0 ); - - $heosCmd = 'playPrev'; - - } elsif ( $cmd =~ /channel/ ) { - - my $favorit = ReadingsVal($name,"channel", 1); - - $favoritcount = scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} ); - $heosCmd = 'playPresetStation'; - + + $heosCmd = 'createGroup'; + + } + elsif ( $cmd eq 'groupClear' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'createGroup'; + + } + elsif ( $cmd eq 'next' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'playNext'; + + } + elsif ( $cmd eq 'prev' ) { + return "usage: $cmd" if ( @args != 0 ); + + $heosCmd = 'playPrev'; + + } + elsif ( $cmd =~ /channel/ ) { + + my $favorit = ReadingsVal( $name, "channel", 1 ); + + $favoritcount = scalar( @{ $hash->{IODev}{helper}{favorites} } ) + if ( defined $hash->{IODev}{helper}{favorites} ); + $heosCmd = 'playPresetStation'; + if ( $cmd eq 'channel' ) { - return "usage: channel 1-$favoritcount" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > $favoritcount || $args[0] < 1); - - $action = "preset=$args[0]"; - - } elsif( $cmd eq 'channelUp' ) { - return "usage: $cmd" if( @args != 0 ); - + return "usage: channel 1-$favoritcount" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > $favoritcount + || $args[0] < 1 ); + + $action = "preset=$args[0]"; + + } + elsif ( $cmd eq 'channelUp' ) { + return "usage: $cmd" if ( @args != 0 ); + ++$favorit; if ( $favorit > $favoritcount ) { - if ( AttrVal($name, 'channelring', 0) == 1 ) { - + if ( AttrVal( $name, 'channelring', 0 ) == 1 ) { + $favorit = 1; - - } else { - + + } + else { + $favorit = $favoritcount; } } - $action = "preset=".$favorit; - - } elsif( $cmd eq 'channelDown' ) { - return "usage: $cmd" if( @args != 0 ); + $action = "preset=" . $favorit; + + } + elsif ( $cmd eq 'channelDown' ) { + return "usage: $cmd" if ( @args != 0 ); --$favorit; if ( $favorit <= 0 ) { - if ( AttrVal($name, 'channelring', 0) == 1 ) { + if ( AttrVal( $name, 'channelring', 0 ) == 1 ) { $favorit = $favoritcount; - } else { + } + else { $favorit = 1; } } - $action = "preset=".$favorit; + $action = "preset=" . $favorit; } - - } elsif ( $cmd =~ /Queue/ ) { - $heosCmd = $cmd; + } + elsif ( $cmd =~ /Queue/ ) { + + $heosCmd = $cmd; if ( $cmd eq 'playQueueItem' ) { - - $qcount = scalar(@{$hash->{helper}{queue}}) if ( defined $hash->{helper}{queue} ); - return "usage: queue 1-$qcount" if( @args != 1 || $args[0] !~ /(\d+)/ || $args[0] > $qcount || $args[0] < 1); - $action = "qid=$args[0]"; - - } elsif ( $cmd eq 'clearQueue' ) { + $qcount = scalar( @{ $hash->{helper}{queue} } ) + if ( defined $hash->{helper}{queue} ); + return "usage: queue 1-$qcount" + if ( @args != 1 + || $args[0] !~ /(\d+)/ + || $args[0] > $qcount + || $args[0] < 1 ); + + $action = "qid=$args[0]"; + + } + elsif ( $cmd eq 'clearQueue' ) { + #löscht die Warteschlange - return "usage: $cmd" if( @args != 0 ); - + return "usage: $cmd" if ( @args != 0 ); + delete $hash->{helper}{queue}; - - } elsif ( $cmd eq 'saveQueue' ) { - + + } + elsif ( $cmd eq 'saveQueue' ) { + #speichert die aktuelle Warteschlange als Playlist ab - return "usage: saveQueue" if( @args != 1 ); - - $action = "name=$args[0]"; + return "usage: saveQueue" if ( @args != 1 ); + + $action = "name=$args[0]"; } - } elsif ( $cmd =~ /Playlist/ ) { - + } + elsif ( $cmd =~ /Playlist/ ) { + my $mid; my $cid = $args[0]; my @path; - @path = split(",", $args[0]) if ( @args != 0 && $args[0] =~ /,/ ); - $cid = $path[0] if ( scalar @path > 0); - $mid = $path[1] if ( scalar @path > 1); + @path = split( ",", $args[0] ) if ( @args != 0 && $args[0] =~ /,/ ); + $cid = $path[0] if ( scalar @path > 0 ); + $mid = $path[1] if ( scalar @path > 1 ); if ( scalar @args != 0 ) { if ( $cid !~ /^-*[0-9]+$/ ) { - - my @cids = map { $_->{cid} } grep { $_->{name} =~ /\Q$cid\E/i } (@{ $hash->{IODev}{helper}{playlists} }); - return "usage: $cmd name" if ( scalar @cids <= 0); - + + my @cids = + map { $_->{cid} } + grep { $_->{name} =~ /\Q$cid\E/i } + ( @{ $hash->{IODev}{helper}{playlists} } ); + return "usage: $cmd name" if ( scalar @cids <= 0 ); + $cid = $cids[0]; } if ( $cmd eq 'playPlaylist' ) { - $heosCmd = $cmd; - $action = "sid=1025&cid=$cid&aid=4"; - - } elsif ( $cmd eq 'playPlaylistItem' ) { - return "usage: playPlaylistItem name,nr" if ( scalar @path < 2); + $heosCmd = $cmd; + $action = "sid=1025&cid=$cid&aid=4"; - $heosCmd = 'playPlaylist'; - $action = "sid=1025&cid=$cid&mid=$mid&aid=4"; - - } elsif ( $cmd eq 'deletePlaylist' ) { - - $heosCmd = $cmd; - $action = "cid=$cid"; - $string = "sid=1025"; } - - } else { - - my @playlists = map { $_->{name} } (@{ $hash->{IODev}{helper}{playlists}}); - return "usage: $cmd name|id".join(",",@playlists); + elsif ( $cmd eq 'playPlaylistItem' ) { + return "usage: playPlaylistItem name,nr" + if ( scalar @path < 2 ); + + $heosCmd = 'playPlaylist'; + $action = "sid=1025&cid=$cid&mid=$mid&aid=4"; + + } + elsif ( $cmd eq 'deletePlaylist' ) { + + $heosCmd = $cmd; + $action = "cid=$cid"; + $string = "sid=1025"; + } + } - - } elsif( $cmd eq 'aux' ) { - return "usage: $cmd" if( @args != 0 ); + else { + + my @playlists = + map { $_->{name} } ( @{ $hash->{IODev}{helper}{playlists} } ); + return "usage: $cmd name|id" . join( ",", @playlists ); + } + + } + elsif ( $cmd eq 'aux' ) { + return "usage: $cmd" if ( @args != 0 ); my $auxname = @{ $hash->{helper}{aux} }[0]->{mid}; $heosCmd = 'playInput'; $action = "input=$auxname"; - - Log3 $name, 4, "HEOSPlayer ($name) - set aux to $auxname"; - readingsSingleUpdate($hash, "input", $args[0], 1); - - } elsif( $cmd eq 'input' ) { - return "usage: $cmd sid[,cid][,mid]" if( @args != 1 ); - my $param = shift( @args ); - my ($sid,$cid,$mid) = split /,/,$param; - return "usage: $cmd sid[,cid][,mid]" unless( defined $sid || $sid eq "" ); + Log3 $name, 4, "HEOSPlayer ($name) - set aux to $auxname"; + readingsSingleUpdate( $hash, "input", $args[0], 1 ); + + } + elsif ( $cmd eq 'input' ) { + return "usage: $cmd sid[,cid][,mid]" if ( @args != 1 ); + + my $param = shift(@args); + my ( $sid, $cid, $mid ) = split /,/, $param; + return "usage: $cmd sid[,cid][,mid]" + unless ( defined $sid || $sid eq "" ); if ( $sid eq "1024" ) { - return "usage: $cmd sid,cid[,mid]" unless( defined($cid) && defined($mid) ); - + return "usage: $cmd sid,cid[,mid]" + unless ( defined($cid) && defined($mid) ); + #Server abspielen $heosCmd = 'playPlaylist'; $action = "sid=$sid&cid=$cid&aid=4"; $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" if ( defined($mid) ); - } elsif ( $sid eq "1025" ) { - return "usage: $cmd sid,cid[,mid]" unless( defined($cid) ); + } + elsif ( $sid eq "1025" ) { + return "usage: $cmd sid,cid[,mid]" unless ( defined($cid) ); #Playlist abspielen $heosCmd = 'playPlaylist'; $action = "sid=$sid&cid=$cid&aid=4"; $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" if ( defined($mid) ); - } elsif ( $sid eq "1026" ) { - return "usage: $cmd sid,cid,mid" unless( defined($cid) ); + } + elsif ( $sid eq "1026" ) { + return "usage: $cmd sid,cid,mid" unless ( defined($cid) ); #Verlauf abspielen if ( $cid eq "TRACKS" ) { $heosCmd = 'playPlaylist'; $action = "sid=$sid&cid=$cid&aid=4"; - $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" if ( defined($mid) ); + $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" + if ( defined($mid) ); - } elsif ( $cid eq "STATIONS" ) { + } + elsif ( $cid eq "STATIONS" ) { $heosCmd = 'playStream'; $action = "sid=$sid&cid=$cid&mid=$mid"; } - } elsif ( $sid eq "1027" ) { - return "usage: $cmd sid,spid,mid" unless( defined($cid) ); + } + elsif ( $sid eq "1027" ) { + return "usage: $cmd sid,spid,mid" unless ( defined($cid) ); #Eingang abspielen $heosCmd = 'playInput'; $action = "input=$mid"; - $action = "spid=$cid&".$action if ( $pid ne $cid ); + $action = "spid=$cid&" . $action if ( $pid ne $cid ); - } elsif ( $sid eq "1028" ) { - return "usage: $cmd sid,nr" unless( defined($cid) ); + } + elsif ( $sid eq "1028" ) { + return "usage: $cmd sid,nr" unless ( defined($cid) ); #Favoriten abspielen $heosCmd = 'playPresetStation'; $action = "preset=$cid"; - } elsif ( $sid eq "1029" ) { - return "usage: $cmd sid,qid" unless( defined($cid) ); + } + elsif ( $sid eq "1029" ) { + return "usage: $cmd sid,qid" unless ( defined($cid) ); #Warteschlange abspielen $heosCmd = 'playQueueItem'; $action = "qid=$cid"; - } elsif ( $sid eq "url" ) { - + } + elsif ( $sid eq "url" ) { + #URL abspielen $heosCmd = 'playStream'; + #$action = "url=".substr($param,4); - $action = "url=$cid"; - - #getestet mit "set HEOSPlayer_Name input url,http://sender.eldoradio.de:8000/128.mp3" ich wollte [cid] nicht nutzen da in einer url ja durchaus mehrere Kommata vorkommen können ob das mit dem substr() so toll ich kann ich leider nicht beurteilen. Auch würde ich bei der $sid ein lc($sid) drum machen aber da es nirgendwo ist :-) - - } else { + $action = "url=$cid"; + +#getestet mit "set HEOSPlayer_Name input url,http://sender.eldoradio.de:8000/128.mp3" ich wollte [cid] nicht nutzen da in einer url ja durchaus mehrere Kommata vorkommen können ob das mit dem substr() so toll ich kann ich leider nicht beurteilen. Auch würde ich bei der $sid ein lc($sid) drum machen aber da es nirgendwo ist :-) + + } + else { if ( $sid > 0 && $sid < 1024 ) { - return "usage: $cmd sid,cid,mid" unless( defined($cid) && defined($mid) ); + return "usage: $cmd sid,cid,mid" + unless ( defined($cid) && defined($mid) ); #Radio abspielen $heosCmd = 'playStream'; - $action = "sid=$sid&cid=$cid&mid=$mid"; + $action = "sid=$sid&cid=$cid&mid=$mid"; + + } + else { + return "usage: $cmd sid,cid[,mid]" unless ( defined($cid) ); - } else { - return "usage: $cmd sid,cid[,mid]" unless( defined($cid) ); - #Server abspielen $heosCmd = 'playPlaylist'; $action = "sid=$sid&cid=$cid&aid=4"; - $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" if ( defined($mid) ); + $action = "sid=$sid&cid=$cid&mid=$mid&aid=4" + if ( defined($mid) ); } } - - } else { - + + } + else { + #### alte get Befehle sollen raus #### getPlayerInfo:noArg getPlayState:noArg getNowPlayingMedia:noArg getPlayMode:noArg - my $list = "play:noArg stop:noArg pause:noArg mute:on,off volume:slider,0,5,100 volumeUp:slider,0,1,10 volumeDown:slider,0,1,10 repeat:one,all,off shuffle:on,off next:noArg prev:noArg input"; + my $list = +"play:noArg stop:noArg pause:noArg mute:on,off volume:slider,0,5,100 volumeUp:slider,0,1,10 volumeDown:slider,0,1,10 repeat:one,all,off shuffle:on,off next:noArg prev:noArg input"; my @players = devspec2array("TYPE=HEOSPlayer:FILTER=NAME!=$name"); - $list .= " groupWithMember:multiple-strict," . join( ",", @players ) if ( scalar @players > 0 ); - $list .= " groupClear:noArg" if ( defined($defs{"HEOSGroup".abs($pid)}) && $defs{"HEOSGroup".abs($pid)}->{STATE} eq "on" ); + $list .= " groupWithMember:multiple-strict," . join( ",", @players ) + if ( scalar @players > 0 ); + $list .= " groupClear:noArg" + if ( defined( $defs{ "HEOSGroup" . abs($pid) } ) + && $defs{ "HEOSGroup" . abs($pid) }->{STATE} eq "on" ); #Parameterlisten für FHEMWeb zusammen bauen - my $favoritcount = scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} ); - if ( defined $favoritcount && $favoritcount > 0) { + my $favoritcount = scalar( @{ $hash->{IODev}{helper}{favorites} } ) + if ( defined $hash->{IODev}{helper}{favorites} ); + if ( defined $favoritcount && $favoritcount > 0 ) { - $list .= " channel:slider,1,1,".scalar(@{$hash->{IODev}{helper}{favorites}}); - $list .= " channelUp:noArg channelDown:noArg" if ( $favoritcount > 1) + $list .= " channel:slider,1,1," + . scalar( @{ $hash->{IODev}{helper}{favorites} } ); + $list .= " channelUp:noArg channelDown:noArg" + if ( $favoritcount > 1 ); } - if ( defined($hash->{helper}{queue}) && ref($hash->{helper}{queue}) eq "ARRAY" && scalar(@{$hash->{helper}{queue}}) > 0 ) { - - $list .= " playQueueItem:slider,1,1,".scalar(@{$hash->{helper}{queue}}) if ( defined $hash->{helper}{queue} ); - $list .= " clearQueue:noArg saveQueue"; + if ( defined( $hash->{helper}{queue} ) + && ref( $hash->{helper}{queue} ) eq "ARRAY" + && scalar( @{ $hash->{helper}{queue} } ) > 0 ) + { + + $list .= + " playQueueItem:slider,1,1," + . scalar( @{ $hash->{helper}{queue} } ) + if ( defined $hash->{helper}{queue} ); + $list .= " clearQueue:noArg saveQueue"; } if ( defined $hash->{IODev}{helper}{playlists} ) { - - my @playlists = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\ /g; $n{name} } (@{ $hash->{IODev}{helper}{playlists}}); - - $list .= " playPlaylist:".join(",",@playlists) if( scalar @playlists > 0 ); - $list .= " deletePlaylist:".join(",",@playlists) if( scalar @playlists > 0 ); + + my @playlists = map { + my %n; + $n{name} = $_->{name}; + $n{name} =~ s/\s+/\ /g; + $n{name} + } ( @{ $hash->{IODev}{helper}{playlists} } ); + + $list .= " playPlaylist:" . join( ",", @playlists ) + if ( scalar @playlists > 0 ); + $list .= " deletePlaylist:" . join( ",", @playlists ) + if ( scalar @playlists > 0 ); } $list .= " aux:noArg" if ( exists $hash->{helper}{aux} ); return "Unknown argument $cmd, choose one of $list"; } - $string .= "&$action" if( defined($action)); - IOWrite($hash,"$heosCmd","$string",undef); - Log3 $name, 4, "HEOSPlayer ($name) - IOWrite: $heosCmd $string IODevHash=$hash->{IODev}"; - return undef; + $string .= "&$action" if ( defined($action) ); + IOWrite( $hash, "$heosCmd", "$string", undef ); + Log3 $name, 4, + "HEOSPlayer ($name) - IOWrite: $heosCmd $string IODevHash=$hash->{IODev}"; + + return; } -sub HEOSPlayer_Parse($$) { +sub HEOSPlayer_Parse { + my ( $io_hash, $json ) = @_; - my ($io_hash,$json) = @_; - my $name = $io_hash->{NAME}; + my $name = $io_hash->{NAME}; my $pid; my $decode_json; my $code; - - $decode_json = eval{decode_json(encode_utf8($json))}; - if($@){ + $decode_json = eval { decode_json( encode_utf8($json) ) }; + if ($@) { Log3 $name, 3, "HEOSPlayer ($name) - JSON error while request: $@"; return; } - + Log3 $name, 4, "HEOSPlayer - ParseFn wurde aufgerufen"; - if( defined($decode_json->{pid}) ) { - - $pid = $decode_json->{pid}; - $code = abs($pid); - $code = $io_hash->{NAME} ."-". $code if( defined($io_hash->{NAME}) ); - - if( my $hash = $modules{HEOSPlayer}{defptr}{$code} ) { - - IOWrite($hash,'getPlayerInfo',"pid=$hash->{PID}",undef); + if ( defined( $decode_json->{pid} ) ) { + + $pid = $decode_json->{pid}; + $code = abs($pid); + $code = $io_hash->{NAME} . "-" . $code + if ( defined( $io_hash->{NAME} ) ); + + if ( my $hash = $modules{HEOSPlayer}{defptr}{$code} ) { + + IOWrite( $hash, 'getPlayerInfo', "pid=$hash->{PID}", undef ); readingsSingleUpdate( $hash, "state", "on", 1 ); - Log3 $hash->{NAME}, 4, "HEOSPlayer ($hash->{NAME}) - find logical device: $hash->{NAME}"; - Log3 $hash->{NAME}, 4, "HEOSPlayer ($hash->{NAME}) - find PID in root from decode_json"; + Log3 $hash->{NAME}, 4, + "HEOSPlayer ($hash->{NAME}) - find logical device: $hash->{NAME}"; + Log3 $hash->{NAME}, 4, + "HEOSPlayer ($hash->{NAME}) - find PID in root from decode_json"; return $hash->{NAME}; - - } else { - - my $devname = "HEOSPlayer".abs($pid); + + } + else { + + my $devname = "HEOSPlayer" . abs($pid); return "UNDEFINED $devname HEOSPlayer $pid IODev=$name"; } - - } else { - - my %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); - $pid = $message{pid} if( defined($message{pid}) ); - $pid = $decode_json->{payload}{pid} if( ref($decode_json->{payload}) ne "ARRAY" && defined($decode_json->{payload}{pid}) ); - + } + else { + + my %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); + + $pid = $message{pid} if ( defined( $message{pid} ) ); + $pid = $decode_json->{payload}{pid} + if ( ref( $decode_json->{payload} ) ne "ARRAY" + && defined( $decode_json->{payload}{pid} ) ); + Log3 $name, 4, "HEOSPlayer ($name) PID: $pid"; - - $code = abs($pid); - $code = $io_hash->{NAME} ."-". $code if( defined($io_hash->{NAME}) ); - - if( my $hash = $modules{HEOSPlayer}{defptr}{$code} ) { - my $name = $hash->{NAME}; - - HEOSPlayer_WriteReadings($hash,$decode_json); - Log3 $name, 4, "HEOSPlayer ($name) - find logical device: $hash->{NAME}"; - + + $code = abs($pid); + $code = $io_hash->{NAME} . "-" . $code + if ( defined( $io_hash->{NAME} ) ); + + if ( my $hash = $modules{HEOSPlayer}{defptr}{$code} ) { + my $name = $hash->{NAME}; + + HEOSPlayer_WriteReadings( $hash, $decode_json ); + Log3 $name, 4, + "HEOSPlayer ($name) - find logical device: $hash->{NAME}"; + return $hash->{NAME}; - - } else { - - my $devname = "HEOSPlayer".abs($pid); + + } + else { + + my $devname = "HEOSPlayer" . abs($pid); return "UNDEFINED $devname HEOSPlayer $pid IODev=$name"; } } + + return; } -sub HEOSPlayer_WriteReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; +sub HEOSPlayer_WriteReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; - Log3 $name, 4, "HEOSPlayer ($name) - processing data to write readings"; ############################ #### Aufbereiten der Daten soweit nötig (bei Events zum Beispiel) - my $readingsHash = HEOSPlayer_PreProcessingReadings($hash,$decode_json) - if( $decode_json->{heos}{message} =~ /^pid=/ and $decode_json->{heos}{command} ne "player\/get_now_playing_media"); + my $readingsHash = HEOSPlayer_PreProcessingReadings( $hash, $decode_json ) + if ( $decode_json->{heos}{message} =~ /^pid=/ + and $decode_json->{heos}{command} ne "player\/get_now_playing_media" ); ############################ #### schreiben der Readings readingsBeginUpdate($hash); ### Event Readings - if( ref($readingsHash) eq "HASH" ) { - - Log3 $name, 4, "HEOSPlayer ($name) - response json Hash back from HEOSPlayer_PreProcessingReadings"; + if ( ref($readingsHash) eq "HASH" ) { + + Log3 $name, 4, +"HEOSPlayer ($name) - response json Hash back from HEOSPlayer_PreProcessingReadings"; my $t; my $v; - - while( ( $t, $v ) = each (%{$readingsHash}) ) { - readingsBulkUpdate( $hash, $t, $v ) if( defined( $v ) ); + + while ( ( $t, $v ) = each( %{$readingsHash} ) ) { + readingsBulkUpdate( $hash, $t, $v ) if ( defined($v) ); } } ### PlayerInfos - readingsBulkUpdate( $hash, 'name', $decode_json->{payload}{name} ); - readingsBulkUpdate( $hash, 'gid', $decode_json->{payload}{gid} ); - readingsBulkUpdate( $hash, 'model', $decode_json->{payload}{model} ); - readingsBulkUpdate( $hash, 'version', $decode_json->{payload}{version} ); - readingsBulkUpdate( $hash, 'network', $decode_json->{payload}{network} ); - readingsBulkUpdate( $hash, 'lineout', $decode_json->{payload}{lineout} ); - readingsBulkUpdate( $hash, 'control', $decode_json->{payload}{control} ); + readingsBulkUpdate( $hash, 'name', $decode_json->{payload}{name} ); + readingsBulkUpdate( $hash, 'gid', $decode_json->{payload}{gid} ); + readingsBulkUpdate( $hash, 'model', $decode_json->{payload}{model} ); + readingsBulkUpdate( $hash, 'version', $decode_json->{payload}{version} ); + readingsBulkUpdate( $hash, 'network', $decode_json->{payload}{network} ); + readingsBulkUpdate( $hash, 'lineout', $decode_json->{payload}{lineout} ); + readingsBulkUpdate( $hash, 'control', $decode_json->{payload}{control} ); readingsBulkUpdate( $hash, 'ip-address', $decode_json->{payload}{ip} ); ### playing Infos readingsBulkUpdate( $hash, 'currentMedia', $decode_json->{payload}{type} ); readingsBulkUpdate( $hash, 'currentTitle', $decode_json->{payload}{song} ); readingsBulkUpdate( $hash, 'currentAlbum', $decode_json->{payload}{album} ); - readingsBulkUpdate( $hash, 'currentArtist', $decode_json->{payload}{artist} ); - readingsBulkUpdate( $hash, 'currentImageUrl', $decode_json->{payload}{image_url} ); + readingsBulkUpdate( $hash, 'currentArtist', + $decode_json->{payload}{artist} ); + readingsBulkUpdate( $hash, 'currentImageUrl', + $decode_json->{payload}{image_url} ); readingsBulkUpdate( $hash, 'currentMid', $decode_json->{payload}{mid} ); readingsBulkUpdate( $hash, 'currentQid', $decode_json->{payload}{qid} ); readingsBulkUpdate( $hash, 'currentSid', $decode_json->{payload}{sid} ); - readingsBulkUpdate( $hash, 'currentStation', $decode_json->{payload}{station} ); + readingsBulkUpdate( $hash, 'currentStation', + $decode_json->{payload}{station} ); - #sucht in den Favoriten nach der aktuell gespielten Radiostation und aktualisiert den channel wenn diese enthalten ist - my @presets = map { $_->{name} } (@{ $hash->{IODev}{helper}{favorites} }); - my $search = ReadingsVal($name,"currentStation" ,undef); - my( @index )= grep { $presets[$_] eq $search } 0..$#presets if ( defined $search ); - - readingsBulkUpdate( $hash, 'channel', $index[0]+1 ) if ( scalar @index > 0 ); +#sucht in den Favoriten nach der aktuell gespielten Radiostation und aktualisiert den channel wenn diese enthalten ist + my @presets = map { $_->{name} } ( @{ $hash->{IODev}{helper}{favorites} } ); + my $search = ReadingsVal( $name, "currentStation", undef ); + my (@index) = grep { $presets[$_] eq $search } 0 .. $#presets + if ( defined $search ); + + readingsBulkUpdate( $hash, 'channel', $index[0] + 1 ) + if ( scalar @index > 0 ); readingsEndUpdate( $hash, 1 ); Log3 $name, 5, "HEOSPlayer ($name) - readings set for $name"; - return undef; -} + return; +} ############### ### my little Helpers -sub HEOSPlayer_PreProcessingReadings($$) { - - my ($hash,$decode_json) = @_; - my $name = $hash->{NAME}; +sub HEOSPlayer_PreProcessingReadings { + my ( $hash, $decode_json ) = @_; + + my $name = $hash->{NAME}; my $reading; my %buffer; - my %message = map { my ( $key, $value ) = split "="; $key => $value } split('&', $decode_json->{heos}{message}); - - + my %message = map { my ( $key, $value ) = split "="; $key => $value } + split( '&', $decode_json->{heos}{message} ); + Log3 $name, 4, "HEOSPlayer ($name) - preprocessing readings"; - - if ( $decode_json->{heos}{command} =~ /play_state/ or $decode_json->{heos}{command} =~ /player_state_changed/ ) { - - $buffer{'playStatus'} = $message{state}; - - } elsif ( $decode_json->{heos}{command} =~ /volume_changed/ or $decode_json->{heos}{command} =~ /set_volume/ or $decode_json->{heos}{command} =~ /get_volume/ ) { - my @value = split('&', $decode_json->{heos}{message}); - $buffer{'volume'} = $message{level}; - $buffer{'mute'} = $message{mute} if( $decode_json->{heos}{command} =~ /volume_changed/ ); - if (defined($buffer{'mute'}) && AttrVal($name, 'mute2play', 0) == 1) { - IOWrite($hash,'setPlayState',"pid=$hash->{PID}&state=play",undef) if $buffer{'mute'} eq "off"; - IOWrite($hash,'setPlayState',"pid=$hash->{PID}&state=stop",undef) if $buffer{'mute'} eq "on"; + if ( $decode_json->{heos}{command} =~ /play_state/ + or $decode_json->{heos}{command} =~ /player_state_changed/ ) + { + + $buffer{'playStatus'} = $message{state}; + + } + elsif ($decode_json->{heos}{command} =~ /volume_changed/ + or $decode_json->{heos}{command} =~ /set_volume/ + or $decode_json->{heos}{command} =~ /get_volume/ ) + { + + my @value = split( '&', $decode_json->{heos}{message} ); + $buffer{'volume'} = $message{level}; + $buffer{'mute'} = $message{mute} + if ( $decode_json->{heos}{command} =~ /volume_changed/ ); + if ( defined( $buffer{'mute'} ) + && AttrVal( $name, 'mute2play', 0 ) == 1 ) + { + IOWrite( $hash, 'setPlayState', "pid=$hash->{PID}&state=play", + undef ) + if $buffer{'mute'} eq "off"; + IOWrite( $hash, 'setPlayState', "pid=$hash->{PID}&state=stop", + undef ) + if $buffer{'mute'} eq "on"; } - - } elsif ( $decode_json->{heos}{command} =~ /play_mode/ or $decode_json->{heos}{command} =~ /repeat_mode_changed/ or $decode_json->{heos}{command} =~ /shuffle_mode_changed/ ) { - - $buffer{'shuffle'} = $message{shuffle}; - $buffer{'repeat'} = $message{repeat}; - $buffer{'repeat'} =~ s/.*\_(.*)/$1/g if( defined($buffer{'repeat'}) ); - - } elsif ( $decode_json->{heos}{command} =~ /get_mute/ ) { - - $buffer{'mute'} = $message{state}; - - } elsif ( $decode_json->{heos}{command} =~ /volume_up/ or $decode_json->{heos}{command} =~ /volume_down/ ) { - - $buffer{'volumeUp'} = $message{step} if( $decode_json->{heos}{command} =~ /volume_up/ ); - $buffer{'volumeDown'} = $message{step} if( $decode_json->{heos}{command} =~ /volume_down/ ); - - } elsif ( $decode_json->{heos}{command} =~ /player_now_playing_changed/ or $decode_json->{heos}{command} =~ /favorites_changed/ ) { - IOWrite($hash,'getNowPlayingMedia',"pid=$hash->{PID}",undef); - - } elsif ( $decode_json->{heos}{command} =~ /play_preset/ ) { - - $buffer{'channel'} = $message{preset} - - } elsif ( $decode_json->{heos}{command} =~ /play_input/ ) { - - $buffer{'input'} = $message{input}; - } elsif ( $decode_json->{heos}{command} =~ /playback_error/ ) { - - $buffer{'error'} = $message{error}; - - } else { - + } + elsif ($decode_json->{heos}{command} =~ /play_mode/ + or $decode_json->{heos}{command} =~ /repeat_mode_changed/ + or $decode_json->{heos}{command} =~ /shuffle_mode_changed/ ) + { + + $buffer{'shuffle'} = $message{shuffle}; + $buffer{'repeat'} = $message{repeat}; + $buffer{'repeat'} =~ s/.*\_(.*)/$1/g + if ( defined( $buffer{'repeat'} ) ); + + } + elsif ( $decode_json->{heos}{command} =~ /get_mute/ ) { + + $buffer{'mute'} = $message{state}; + + } + elsif ($decode_json->{heos}{command} =~ /volume_up/ + or $decode_json->{heos}{command} =~ /volume_down/ ) + { + + $buffer{'volumeUp'} = $message{step} + if ( $decode_json->{heos}{command} =~ /volume_up/ ); + $buffer{'volumeDown'} = $message{step} + if ( $decode_json->{heos}{command} =~ /volume_down/ ); + + } + elsif ($decode_json->{heos}{command} =~ /player_now_playing_changed/ + or $decode_json->{heos}{command} =~ /favorites_changed/ ) + { + IOWrite( $hash, 'getNowPlayingMedia', "pid=$hash->{PID}", undef ); + + } + elsif ( $decode_json->{heos}{command} =~ /play_preset/ ) { + + $buffer{'channel'} = $message{preset}; + + } + elsif ( $decode_json->{heos}{command} =~ /play_input/ ) { + + $buffer{'input'} = $message{input}; + + } + elsif ( $decode_json->{heos}{command} =~ /playback_error/ ) { + + $buffer{'error'} = $message{error}; + + } + else { + Log3 $name, 4, "HEOSPlayer ($name) - no match found"; return undef; } - + Log3 $name, 4, "HEOSPlayer ($name) - Match found for decode_json"; + return \%buffer; } -sub HEOSPlayer_GetPlayerInfo($) { +sub HEOSPlayer_GetPlayerInfo { + my $hash = shift; - my $hash = shift; + RemoveInternalTimer( $hash, 'HEOSPlayer_GetPlayerInfo' ); - - RemoveInternalTimer($hash,'HEOSPlayer_GetPlayerInfo'); - IOWrite($hash,'getPlayerInfo',"pid=$hash->{PID}",undef); + return IOWrite( $hash, 'getPlayerInfo', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetPlayState($) { - - my $hash = shift; +sub HEOSPlayer_GetPlayState { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetPlayState'); - IOWrite($hash,'getPlayState',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetPlayState' ); + + return IOWrite( $hash, 'getPlayState', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetPlayMode($) { - - my $hash = shift; +sub HEOSPlayer_GetPlayMode { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetPlayMode'); - IOWrite($hash,'getPlayMode',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetPlayMode' ); + + return IOWrite( $hash, 'getPlayMode', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetNowPlayingMedia($) { - - my $hash = shift; +sub HEOSPlayer_GetNowPlayingMedia { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetNowPlayingMedia'); - IOWrite($hash,'getNowPlayingMedia',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetNowPlayingMedia' ); + + return IOWrite( $hash, 'getNowPlayingMedia', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetVolume($) { - - my $hash = shift; +sub HEOSPlayer_GetVolume { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetVolume'); - IOWrite($hash,'getVolume',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetVolume' ); + + return IOWrite( $hash, 'getVolume', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetMute($) { - - my $hash = shift; +sub HEOSPlayer_GetMute { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetMute'); - IOWrite($hash,'getMute',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetMute' ); + + return IOWrite( $hash, 'getMute', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_GetQueue($) { - - my $hash = shift; +sub HEOSPlayer_GetQueue { + my $hash = shift; - - RemoveInternalTimer($hash,'HEOSPlayer_GetQueue'); - IOWrite($hash,'getQueue',"pid=$hash->{PID}",undef); + RemoveInternalTimer( $hash, 'HEOSPlayer_GetQueue' ); + + return IOWrite( $hash, 'getQueue', "pid=$hash->{PID}", undef ); } -sub HEOSPlayer_MakePlayLink($$$$$$$$) { +sub HEOSPlayer_MakePlayLink { + my ( $type, $name, $sid, $itemtype, $itemname, $itemurl, $xsize, $ysize ) = + @_; - my ($type, $name, $sid, $itemtype, $itemname, $itemurl, $xsize, $ysize) = @_; - - if( $type eq 'FHEMWEB' ) { + if ( $type eq 'FHEMWEB' ) { - my $xcmd = 'cmd'.uri_escape('=get '.$name.' ls '.$sid); + my $xcmd = 'cmd' . uri_escape( '=get ' . $name . ' ls ' . $sid ); my $xtext = $sid; - - $xcmd = 'cmd'.uri_escape('=set '.$name.' input '.$sid) if ( $itemtype eq "heos_queue" ); - $ysize = '10.75em' if (!defined($ysize)); - $xcmd = "FW_cmd('$FW_ME$FW_subdir?XHR=1&$xcmd')"; - - return '
    '.$itemname."
    "; - } else { + $xcmd = 'cmd' . uri_escape( '=set ' . $name . ' input ' . $sid ) + if ( $itemtype eq "heos_queue" ); + $ysize = '10.75em' if ( !defined($ysize) ); + $xcmd = "FW_cmd('$FW_ME$FW_subdir?XHR=1&$xcmd')"; + + return + '
    ' + . $itemname + . "
    "; + + } + else { return sprintf( "%-15s %s\n", $sid, $itemname ); } + return; } -sub HEOSPlayer_makeImage($$) { - my ($url, $xsize, $ysize) = @_; +sub HEOSPlayer_makeImage { + my ( $url, $xsize, $ysize ) = @_; - my $ret .= "\n"; + my $ret .= +"\n"; return $ret; } - - - - 1; - - - - =pod =item device =item summary Modul to controls the Denon multiroom soundsystem diff --git a/controls_HEOS.txt b/controls_HEOS.txt new file mode 100644 index 0000000..e69de29 diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100755 index 0000000..688c176 --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w + +use File::Basename; +use POSIX qw(strftime); +use strict; + +my @filenames = + ( 'FHEM/21_HEOSGroup.pm', 'FHEM/21_HEOSMaster.pm', 'FHEM/21_HEOSPlayer.pm' ); + +my $controlsfile = 'controls_HEOS.txt'; + +open( FH, ">$controlsfile" ) || return ("Can't open $controlsfile: $!"); + +for my $filename (@filenames) { + my @statOutput = stat($filename); + + if ( scalar @statOutput != 13 ) { + printf 'error: stat has unexpected return value for ' + . $filename . "\n"; + next; + } + + my $mtime = $statOutput[9]; + my $date = POSIX::strftime( "%Y-%m-%d", localtime($mtime) ); + my $time = POSIX::strftime( "%H:%M:%S", localtime($mtime) ); + my $filetime = $date . "_" . $time; + + my $filesize = $statOutput[7]; + + printf FH 'UPD ' . $filetime . ' ' . $filesize . ' ' . $filename . "\n"; +} + +close(FH); + +system("git add $controlsfile"); + +print 'Create controls File succesfully' . "\n"; + +exit 0;