Formatierung, diverse Bugfixes

This commit is contained in:
Marko Oldenburg 2017-02-27 09:39:48 +01:00
parent bc9eb8ec8e
commit 80a84377e2
3 changed files with 700 additions and 133 deletions

View File

@ -36,7 +36,7 @@ use warnings;
use JSON qw(decode_json);
use Encode qw(encode_utf8);
my $version = "0.1.58";
my $version = "0.1.60";
# Declare functions
sub HEOSGroup_Initialize($);
@ -53,10 +53,12 @@ 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";
@ -69,26 +71,32 @@ sub HEOSGroup_Initialize($) {
$readingFnAttributes;
foreach my $d(sort keys %{$modules{HEOSGroup}{defptr}}) {
my $hash = $modules{HEOSGroup}{defptr}{$d};
$hash->{VERSION} = $version;
}
}
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]*)/ ) {
$iodev = $1;
splice( @a, $i, 3 );
last;
}
$i++;
}
return "too few parameters: define <name> HEOSGroup <gid>" if( @a < 2 );
my ($name,$gid) = @a;
@ -97,73 +105,96 @@ sub HEOSGroup_Define($$) {
$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) );
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 );
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 );
}
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state','Initialized');
readingsBulkUpdate($hash, 'volumeUp', 5);
readingsBulkUpdate($hash, 'volumeDown', 5);
readingsEndUpdate($hash, 1);
$modules{HEOSGroup}{defptr}{$code} = $hash;
return 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});
Log3 $name, 3, "HEOSGroup ($name) - device $name deleted with Code: $code";
return undef;
}
sub HEOSGroup_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
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 );
Log3 $name, 3, "HEOSGroup ($name) - disabled";
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSGroup ($name) - enabled";
}
}
if( $attrName eq "disabledForIntervals" ) {
if( $cmd eq "set" ) {
Log3 $name, 3, "HEOSGroup ($name) - enable disabledForIntervals";
readingsSingleUpdate ( $hash, "state", "Unknown", 1 );
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSGroup ($name) - delete disabledForIntervals";
}
@ -171,27 +202,33 @@ sub HEOSGroup_Attr(@) {
}
sub HEOSGroup_Notify($$) {
my ($hash,$dev) = @_;
my $name = $hash->{NAME};
return undef if(IsDisabled($name));
my $events = deviceEvents($dev,1);
#print "notify ####################################################\n".Dumper($events);
#print "notify ####################################################\n".Dumper($events);
return if( !$events );
readingsBeginUpdate($hash);
#my %playerEevents = map { my ( $key, $value ) = split ":"; $value =~ s/^\s+//; ( $key, $value ) } @$events;
my %playerEevents = map { my ( $key, $value ) = split /:\s/; $value =~ s/^\s+//; ( $key, $value ) } @$events;
my %playerEevents = map { my ( $key, $value ) = split /:\s/; $value =~ s/^\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" ) );
}
readingsEndUpdate( $hash, 1 );
}
sub HEOSGroup_Set($$@) {
my ($hash, $name, @aa) = @_;
my ($cmd, @args) = @aa;
my $gid = $hash->{GID};
@ -202,110 +239,156 @@ 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: getGroupInfo" if( @args != 0 );
$heosCmd = $cmd;
} elsif( $cmd eq 'mute' ) {
return "usage: mute on/off" if( @args != 1 );
$heosCmd = 'setGroupMute';
$action = "state=$args[0]";
} elsif( $cmd eq 'volume' ) {
return "usage: volume 0-100" if( @args != 1 );
$heosCmd = 'setGroupVolume';
$action = "level=$args[0]";
} elsif( $cmd eq 'volumeUp' ) {
return "usage: volumeUp 0-10" if( @args != 1 );
$heosCmd = 'GroupVolumeUp';
$action = "step=$args[0]";
} elsif( $cmd eq 'volumeDown' ) {
return "usage: volumeDown 0-10" if( @args != 1 );
$heosCmd = 'groupVolumeDown';
$action = "step=$args[0]";
} elsif( $cmd eq 'clearGroup' ) {
return "usage: clearGroup" if( @args != 0 );
$heosCmd = 'createGroup';
$string = "pid=$gid";
} elsif( grep { $_ =~ /\Q$cmd\E/ } ("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 );
$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: 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 eq 'channel' ) {
$favoritcount = scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} );
return "usage: channel 1-$favoritcount" if( @args != 1 );
$heosCmd = 'playPresetStation';
$action = "preset=$args[0]";
} elsif( $cmd eq 'channelUp' ) {
return "usage: channelUp" if( @args != 0 );
$favoritcount = scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} );
$heosCmd = 'playPresetStation';
$favorit = ReadingsVal($name,"channel", 0) + 1;
$favorit = $favoritcount if ( $favorit > $favoritcount);
$action = "preset=".$favorit;
} elsif( $cmd eq 'channelDown' ) {
return "usage: channelDown" if( @args != 0 );
$heosCmd = 'playPresetStation';
$favorit = ReadingsVal($name,"channel", 0) - 1;
$favorit = 1 if ($favorit <= 0);
$action = "preset=".$favorit;
} elsif ( $cmd =~ /Playlist/ ) {
my @cids = map { $_->{cid} } grep { $_->{name} =~ /$args[0]/i } (@{ $hash->{IODev}{helper}{playlists} });
my @cids = map { $_->{cid} } grep { $_->{name} =~ /$args[0]/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";
}
} else {
IOWrite($hash,'browseSource','sid=1025');
my @playlists = map { $_->{name} } (@{ $hash->{IODev}{helper}{playlists}});
return "usage: $cmd ".join(",",@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";
}
} 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 ";
$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+/\&nbsp;/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}";
@ -313,42 +396,58 @@ sub HEOSGroup_Set($$@) {
}
sub HEOSGroup_Parse($$) {
my ($io_hash,$json) = @_;
my $name = $io_hash->{NAME};
my $gid;
my $decode_json;
my $code;
my $code;
$decode_json = decode_json(encode_utf8($json));
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}");
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);
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}) );
Log3 $name, 4, "HEOSGroup ($name) - GID: $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}";
return $hash->{NAME};
} else {
my $devname = "HEOSGroup".abs($gid);
return "UNDEFINED $devname HEOSGroup $gid IODev=$name";
}
@ -356,9 +455,11 @@ sub HEOSGroup_Parse($$) {
}
sub HEOSGroup_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "HEOSGroup ($name) - processing data to write readings";
############################
#### Aufbereiten der Daten soweit nötig (bei Events zum Beispiel)
@ -370,31 +471,44 @@ sub HEOSGroup_WriteReadings($$) {
readingsBeginUpdate($hash);
### Event Readings
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 ) );
}
}
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" ) {
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" );
}
if ( scalar @members > 1 ) {
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;
}
@ -403,52 +517,68 @@ sub HEOSGroup_WriteReadings($$) {
### my little Helpers
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});
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});
$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 {
Log3 $name, 3, "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;
RemoveInternalTimer($hash,'HEOSGroup_GetGroupInfo');
IOWrite($hash,'getGroupInfo',"gid=$hash->{GID}");
}
sub HEOSGroup_GetGroupVolume($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSGroup_GetGroupVolume');
IOWrite($hash,'getGroupVolume',"gid=$hash->{GID}");
}
sub HEOSGroup_GetGroupMute($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSGroup_GetGroupMute');
IOWrite($hash,'getGroupMute',"gid=$hash->{GID}");
}

View File

@ -47,7 +47,7 @@ use Encode qw(encode_utf8);
use Net::Telnet;
use Data::Dumper;
my $version = "0.1.58";
my $version = "0.1.60";
my %heosCmds = (
'enableChangeEvents' => 'system/register_for_change_events?enable=',
@ -126,8 +126,10 @@ sub HEOSMaster_GetPlaylists($);
sub HEOSMaster_GetServers($);
sub HEOSMaster_Initialize($) {
my ($hash) = @_;
# Provider
$hash->{ReadFn} = "HEOSMaster_Read";
$hash->{WriteFn} = "HEOSMaster_Write";
@ -147,15 +149,18 @@ sub HEOSMaster_Initialize($) {
$readingFnAttributes;
foreach my $d(sort keys %{$modules{HEOSMaster}{defptr}}) {
my $hash = $modules{HEOSMaster}{defptr}{$d};
$hash->{VERSION} = $version;
}
}
sub HEOSMaster_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
return "too few parameters: define <name> HEOSMaster <HOST>" if( @a != 3 );
my $name = $a[0];
@ -173,118 +178,159 @@ sub HEOSMaster_Define($$) {
readingsEndUpdate($hash,1);
if( $init_done ) {
HEOSMaster_firstRun($hash);
} else {
InternalTimer( gettimeofday()+15, 'HEOSMaster_firstRun', $hash, 0 ) if( ($hash->{HOST}) );
}
$modules{HEOSPlayer}{defptr}{$host} = $hash;
return undef;
}
sub HEOSMaster_Undef($$) {
my ( $hash, $arg ) = @_;
my $host = $hash->{HOST};
my $name = $hash->{NAME};
HEOSMaster_Close($hash);
delete $modules{HEOSMaster}{defptr}{$hash->{HOST}};
Log3 $name, 3, "HEOSPlayer ($name) - device $name deleted";
return undef;
}
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 );
Log3 $name, 3, "HEOSMaster ($name) - disabled";
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSMaster ($name) - enabled";
}
}
if( $attrName eq "disabledForIntervals" ) {
if( $cmd eq "set" ) {
Log3 $name, 3, "HEOSMaster ($name) - enable disabledForIntervals";
readingsSingleUpdate ( $hash, "state", "Unknown", 1 );
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSMaster ($name) - delete disabledForIntervals";
}
}
return undef;
}
sub HEOSMaster_Get($$@) {
my ($hash, $name, @aa) = @_;
my ($cmd, @args) = @aa;
my $pid = $hash->{PID};
if( $cmd eq 'showAccount' ) {
return AttrVal($name,'heosUsername',0) . ":" .HEOSMaster_ReadPassword($hash);
}
my $list = 'showAccount:noArg';
return "Unknown argument $cmd, choose one of $list";
}
sub HEOSMaster_Set($@) {
my ($hash, $name, $cmd, @args) = @_;
my ($arg, @params) = @args;
my $action;
my $heosCmd;
if($cmd eq 'reopen') {
return "usage: reopen" if( @args != 0 );
HEOSMaster_ReOpen($hash);
return undef;
} elsif($cmd eq 'getPlayers') {
return "usage: getPlayers" if( @args != 0 );
$heosCmd = 'getPlayers';
$action = undef;
} elsif($cmd eq 'getGroups') {
return "usage: getGroups" if( @args != 0 );
$heosCmd = 'getGroups';
$action = undef;
} elsif($cmd eq 'enableChangeEvents') {
return "usage: enableChangeEvents" if( @args != 1 );
$heosCmd = $cmd;
$action = $args[0];
} elsif($cmd eq 'checkAccount') {
return "usage: checkAccount" if( @args != 0 );
$heosCmd = $cmd;
$action = undef;
} elsif($cmd eq 'signAccount') {
return "usage: signAccountIn" if( @args != 1 );
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) if($args[0] eq 'In');
} elsif($cmd eq 'password') {
return "usage: password" if( @args != 1 );
return HEOSMaster_StorePassword( $hash, $args[0] );
} elsif($cmd eq 'reboot') {
return "usage: reboot" if( @args != 0 );
return HEOSMaster_StorePassword( $hash, $args[0] );
###################################################
### Dieser Menüpunkt ist nur zum testen
###################################################
### Dieser Menüpunkt ist nur zum testen
} elsif($cmd eq 'eventSend') {
return "usage: eventSend" if( @args != 0 );
HEOSMaster_send($hash);
return 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);
}
sub HEOSMaster_Open($) {
my $hash = shift;
my $name = $hash->{NAME};
my $host = $hash->{HOST};
@ -293,6 +339,7 @@ sub HEOSMaster_Open($) {
my $user = AttrVal($name,'heosUsername',undef);
my $password = HEOSMaster_ReadPassword($hash);
Log3 $name, 4, "HEOSMaster ($name) - Baue Socket Verbindung auf";
my $socket = new Net::Telnet ( Host=>$host,
@ -314,9 +361,11 @@ sub HEOSMaster_Open($) {
#hinzugefügt laut Protokoll 2.1.1 Initsequenz
if( defined($user) and defined($password) ) {
HEOSMaster_Write($hash,'signAccountIn',"un=$user&pw=$password");
Log3 $name, 4, "HEOSMaster ($name) - sign in";
}
HEOSMaster_GetPlayers($hash);
InternalTimer( gettimeofday()+1, 'HEOSMaster_EnableChangeEvents', $hash, 0 );
InternalTimer( gettimeofday()+2, 'HEOSMaster_GetMusicSources', $hash, 0 );
@ -324,93 +373,131 @@ sub HEOSMaster_Open($) {
}
sub HEOSMaster_Close($) {
my $hash = shift;
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 );
}
sub HEOSMaster_ReOpen($) {
my $hash = shift;
my $name = $hash->{NAME};
HEOSMaster_Close($hash);
HEOSMaster_Open($hash) if( !$hash->{CD} or !defined($hash->{CD}) );
}
sub HEOSMaster_Write($@) {
my ($hash,$heosCmd,$value) = @_;
my $name = $hash->{NAME};
my $string = "heos://$heosCmds{$heosCmd}";
if( defined($value) ) {
$string .= "${value}" if( $value ne '&' );
}
$string .= "\r\n";
Log3 $name, 4, "HEOSMaster ($name) - WriteFn called";
return Log3 $name, 4, "HEOSMaster ($name) - socket not connected"
unless($hash->{CD});
Log3 $name, 5, "HEOSMaster ($name) - $string";
syswrite($hash->{CD}, $string);
return undef;
}
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); # die genaue Puffergröße wird noch ermittelt
if( !defined($len) || !$len ) {
Log3 $name, 5, "HEOSMaster ($name) - connection closed by remote Host";
HEOSMaster_Close($hash);
return;
}
unless( defined $buf) {
Log3 $name, 3, "HEOSMaster ($name) - Keine Daten empfangen";
return;
}
Log3 $name, 5, "HEOSMaster ($name) - received buffer data, start HEOSMaster_ProcessRead: $buf";
HEOSMaster_ProcessRead($hash,$buf);
}
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}) {
Log3 $name, 5, "HEOSMaster ($name) - PARTIAL: " . $hash->{PARTIAL};
$buffer = $hash->{PARTIAL};
} 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 = JSON->new->utf8(0)->decode($json);
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;
}
($json,$tail) = HEOSMaster_ParseMsg($hash, $tail);
}
$hash->{PARTIAL} = $tail;
Log3 $name, 5, "HEOSMaster ($name) - Tail: " . $tail;
Log3 $name, 5, "HEOSMaster ($name) - PARTIAL: " . $hash->{PARTIAL};
@ -418,10 +505,12 @@ sub HEOSMaster_ProcessRead($$) {
}
sub HEOSMaster_ResponseProcessing($$) {
my ($hash,$json) = @_;
my $name = $hash->{NAME};
my $decode_json;
Log3 $name, 5, "HEOSMaster ($name) - JSON String: $json";
return Log3 $name, 3, "HEOSMaster ($name) - empty answer received"
unless( defined($json));
@ -436,6 +525,7 @@ sub HEOSMaster_ResponseProcessing($$) {
if( $decode_json->{heos}{message} =~ /command\sunder\sprocess/ );
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";
}
@ -447,24 +537,28 @@ sub HEOSMaster_ResponseProcessing($$) {
#Quellen neu einlesen
if( $decode_json->{heos}{command} =~ /^event\/sources_changed/ ) {
HEOSMaster_Write($hash,'getMusicSources',undef);
return Log3 $name, 4, "HEOSMaster ($name) - source changed";
}
#Player neu einlesen
if( $decode_json->{heos}{command} =~ /^event\/players_changed/ ) {
HEOSMaster_Write($hash,'getPlayers',undef);
return Log3 $name, 4, "HEOSMaster ($name) - player changed";
}
#User neu einlesen
if( $decode_json->{heos}{command} =~ /^event\/user_changed/ ) {
HEOSMaster_Write($hash,'checkAccount',undef);
return Log3 $name, 4, "HEOSMaster ($name) - user changed";
}
#Gruppen neu einlesen
if( $decode_json->{heos}{command} =~ /^event\/groups_changed/ ) {
#InternalTimer( gettimeofday()+5, 'HEOSMaster_GetGroups', $hash, 0 );
HEOSMaster_Write($hash,'getGroups',undef);
return Log3 $name, 4, "HEOSMaster ($name) - groups changed";
@ -478,78 +572,113 @@ sub HEOSMaster_ResponseProcessing($$) {
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}";
}
}
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 ( defined $message{sid} ) {
#my @range = ( 0 );
#@range = split(',', $message{range}) if ( defined $message{range} );
if ( defined $message{range} ) { $message{range} =~ s/(\d+)\,\d+/$1/; }
else { $message{range} = 0; }
if ( defined $message{range} ) {
$message{range} =~ s/(\d+)\,\d+/$1/;
} else {
$message{range} = 0;
}
my $start = $message{range} + $message{returned};
if( $message{sid} eq '1028' ) {
#Favoriten einlesen
$hash->{helper}{favorites} = [] if ( $message{range} == 0 );
push( @{$hash->{helper}{favorites}}, (@{$decode_json->{payload}}) );
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";
}
}
} elsif( $message{sid} eq '1026' ) {
#History einlesen
$hash->{helper}{history} = [] if ( $message{range} == 0 );
push( @{$hash->{helper}{history}}, (@{$decode_json->{payload}}) );
} 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 '1027' ) {
#Inputs einlesen
push( @{$hash->{helper}{sources}}, map { $_->{name} .= " AUX"; $_ } (@{$decode_json->{payload}}) );
} elsif( $message{sid} eq '1024' ) {
#Lokal einlesen
push( @{$hash->{helper}{sources}}, map { $_->{name} .= " USB" if ( $_->{sid} < 0 ); $_ } (@{$decode_json->{payload}}) );
} else {
#aktuellen Input/Media einlesen
$hash->{helper}{media} = [] if ( $message{range} == 0 );
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} ) {
HEOSMaster_Write($hash,'browseSource',"sid=$message{sid}&range=$start,".($start + 100) );
Log3 $name, 3, "HEOSMaster ($name) - call Browser with sid $message{sid} next Range from $message{returned}";
}
return;
}
}
@ -557,22 +686,29 @@ sub HEOSMaster_ResponseProcessing($$) {
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/ ) {
return Log3 $name, 4, "HEOSMaster ($name) - empty ARRAY received"
unless(scalar(@{$decode_json->{payload}}) > 0);
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";
}
} elsif( $decode_json->{heos}{command} =~ /group\/get_groups/ ) {
my $filter = "TYPE=HEOSGroup";
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"}}';
@ -580,40 +716,52 @@ sub HEOSMaster_ResponseProcessing($$) {
Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for Groups";
$filter .= $payload->{gid}."|";
}
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_queue/ ) {
return Log3 $name, 4, "HEOSMaster ($name) - empty ARRAY received"
unless(scalar(@{$decode_json->{payload}}) > 0);
Dispatch($hash,$json,undef);
Log3 $name, 4, "HEOSMaster ($name) - call Dispatcher for QueueInfo";
#} elsif( defined($decode_json->{payload}{pid}) ) {
} 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( defined($decode_json->{payload}{gid}) and defined($decode_json->{payload}{players}) ) {
} 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( 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";
}
sub HEOSMaster_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
############################
#### Aufbereiten der Daten soweit nötig
my $readingsHash = HEOSMaster_PreProcessingReadings($hash,$decode_json)
@ -629,20 +777,26 @@ sub HEOSMaster_WriteReadings($$) {
### Event Readings
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) );
}
}
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;
}
@ -651,6 +805,7 @@ sub HEOSMaster_WriteReadings($$) {
### my little Helpers
sub HEOSMaster_ParseMsg($$) {
my ($hash, $buffer) = @_;
my $name = $hash->{NAME};
my $open = 0;
@ -658,169 +813,220 @@ sub HEOSMaster_ParseMsg($$) {
my $msg = '';
my $tail = '';
if($buffer) {
foreach my $c (split //, $buffer) {
if($open == $close && $open > 0) {
$tail .= $c;
#Log3 $name, 5, "HEOSMaster ($name) - $open == $close && $open > 0";
} elsif(($open == $close) && ($c ne '{')) {
Log3 $name, 5, "HEOSMaster ($name) - Garbage character before message: " . $c;
} else {
if($c eq '{') {
$open++;
} elsif($c eq '}') {
$close++;
}
$msg .= $c;
}
}
if($open != $close) {
$tail = $msg;
$msg = '';
}
}
Log3 $name, 5, "HEOSMaster ($name) - return msg: $msg and tail: $tail";
return ($msg,$tail);
}
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} ) {
$buffer{'heosAccount'} = "signed_out";
} 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};
RemoveInternalTimer($hash,'HEOSMaster_firstRun');
HEOSMaster_Open($hash) if( !IsDisabled($name) );
}
sub HEOSMaster_GetPlayers($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash,'HEOSMaster_GetPlayers');
HEOSMaster_Write($hash,'getPlayers',undef);
Log3 $name, 4, "HEOSMaster ($name) - getPlayers";
}
sub HEOSMaster_GetGroups($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash,'HEOSMaster_GetGroups');
HEOSMaster_Write($hash,'getGroups',undef);
Log3 $name, 4, "HEOSMaster ($name) - getGroups";
}
sub HEOSMaster_EnableChangeEvents($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash,'HEOSMaster_EnableChangeEvents');
HEOSMaster_Write($hash,'enableChangeEvents','on');
Log3 $name, 4, "HEOSMaster ($name) - set enableChangeEvents on";
}
sub HEOSMaster_GetMusicSources($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetMusicSources');
HEOSMaster_Write($hash,'getMusicSources',undef);
Log3 $name, 4, "HEOSMaster ($name) - getMusicSources";
}
sub HEOSMaster_GetFavorites($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetFavorites');
HEOSMaster_Write($hash,'browseSource','sid=1028');
Log3 $name, 4, "HEOSMaster ($name) - getFavorites";
}
sub HEOSMaster_GetInputs($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetInputs');
HEOSMaster_Write($hash,'browseSource','sid=1027');
Log3 $name, 4, "HEOSMaster ($name) - getInputs";
}
sub HEOSMaster_GetServers($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetServers');
HEOSMaster_Write($hash,'browseSource','sid=1024');
Log3 $name, 4, "HEOSMaster ($name) - getServers";
}
sub HEOSMaster_GetPlaylists($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetPlaylists');
HEOSMaster_Write($hash,'browseSource','sid=1025');
Log3 $name, 4, "HEOSMaster ($name) - getPlaylists";
}
sub HEOSMaster_GetHistory($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_GetHistory');
HEOSMaster_Write($hash,'browseSource','sid=1026');
Log3 $name, 4, "HEOSMaster ($name) - getHistory";
}
sub HEOSMaster_CheckAccount($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, 'HEOSMaster_CheckAccount');
HEOSMaster_Write($hash,'checkAccount',undef);
Log3 $name, 4, "HEOSMaster ($name) - checkAccount";
}
sub HEOSMaster_StorePassword($$) {
my ($hash, $password) = @_;
my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd";
my $key = getUniqueId().$index;
my $enc_pwd = "";
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;
}
my $err = setKeyValue($index, $enc_pwd);
return "error while saving the password - $err" if(defined($err));
@ -828,41 +1034,57 @@ sub HEOSMaster_StorePassword($$) {
}
sub HEOSMaster_ReadPassword($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd";
my $key = getUniqueId().$index;
my ($password, $err);
Log3 $name, 4, "HEOSMaster ($name) - Read FritzBox password from file";
Log3 $name, 4, "HEOSMaster ($name) - Read password from file";
($err, $password) = getKeyValue($index);
if ( defined($err) ) {
Log3 $name, 4, "HEOSMaster ($name) - unable to read FritzBox password from file: $err";
Log3 $name, 4, "HEOSMaster ($name) - unable to read password from file: $err";
return undef;
}
if ( defined($password) ) {
if ( eval "use Digest::MD5;1" ) {
$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;
}
return $dec_pwd;
} else {
Log3 $name, 4, "HEOSMaster ($name) - No password in file";
return undef;
Log3 $name, 4, "HEOSMaster ($name) - No password in file";
return undef;
}
}
################
### Nur für mich um dem Emulator ein Event ab zu jagen
sub HEOSMaster_send($) {
my $hash = shift;
HEOSMaster_Write($hash,'eventChangeVolume',undef);
}

View File

@ -37,7 +37,7 @@ use JSON qw(decode_json);
use Encode qw(encode_utf8);
use Data::Dumper;
my $version = "0.1.58";
my $version = "0.1.60";
# Declare functions
sub HEOSPlayer_Initialize($);
@ -57,10 +57,12 @@ sub HEOSPlayer_Get($$@);
sub HEOSPlayer_GetMute($);
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";
@ -74,57 +76,73 @@ sub HEOSPlayer_Initialize($) {
$readingFnAttributes;
foreach my $d(sort keys %{$modules{HEOSPlayer}{defptr}}) {
my $hash = $modules{HEOSPlayer}{defptr}{$d};
$hash->{VERSION} = $version;
}
}
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]*)/ ) {
$iodev = $1;
splice( @a, $i, 3 );
last;
}
$i++;
}
return "too few parameters: define <name> HEOSPlayer <pid>" 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) );
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 );
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(12)), "HEOSPlayer_GetMute", $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 );
@ -132,49 +150,61 @@ sub HEOSPlayer_Define($$) {
InternalTimer( gettimeofday()+15+int(rand(10)), "HEOSPlayer_GetVolume", $hash, 0 );
InternalTimer( gettimeofday()+15+int(rand(12)), "HEOSPlayer_GetMute", $hash, 0 );
}
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state','Initialized');
readingsBulkUpdate($hash, 'volumeUp', 5);
readingsBulkUpdate($hash, 'volumeDown', 5);
readingsEndUpdate($hash, 1);
$modules{HEOSPlayer}{defptr}{$code} = $hash;
return undef;
}
sub HEOSPlayer_Undef($$) {
my ( $hash, $arg ) = @_;
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});
Log3 $name, 3, "HEOSPlayer ($name) - device $name deleted with Code: $code";
return undef;
}
sub HEOSPlayer_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
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 );
Log3 $name, 3, "HEOSPlayer ($name) - disabled";
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSPlayer ($name) - enabled";
}
}
if( $attrName eq "disabledForIntervals" ) {
if( $cmd eq "set" ) {
Log3 $name, 3, "HEOSPlayer ($name) - enable disabledForIntervals";
readingsSingleUpdate ( $hash, "state", "Unknown", 1 );
}
elsif( $cmd eq "del" ) {
} elsif( $cmd eq "del" ) {
readingsSingleUpdate ( $hash, "state", "active", 1 );
Log3 $name, 3, "HEOSPlayer ($name) - delete disabledForIntervals";
}
@ -182,48 +212,58 @@ sub HEOSPlayer_Attr(@) {
}
sub HEOSPlayer_Get($$@) {
my ($hash, $name, @aa) = @_;
my ($cmd, @args) = @aa;
my $pid = $hash->{PID};
my $result = "";
#print "CL ###################################################\n".Dumper($hash->{CL});
$hash->{helper}{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' );
#print "CL ###################################################\n".Dumper($hash->{CL});
$hash->{helper}{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' );
#Leerzeichen müßen für die Rückgabe escaped werden sonst werden sie falsch angezeigt
if( $cmd eq 'playlists' ) {
#gibt die Playlisten durch Komma getrennt zurück
my @playlists = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{playlists}});
$result .= join(",",@playlists) if( scalar @playlists > 0 );
return $result;
} elsif( $cmd eq 'channels' ) {
#gibt die Favoriten durch Komma getrennt zurück
my @channels = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{favorites}});
$result .= join(",",@channels) if( scalar @channels > 0 );
return $result;
} elsif( $cmd eq 'channelscount' ) {
#gibt die Favoritenanzahl zurück
return scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} );
} elsif( $cmd eq 'inputs' ) {
#gibt die Quellen durch Komma getrennt zurück
my @inputs = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{sources}});
push(@inputs, "Warteschlange");
$result .= join(",",@inputs) if( scalar @inputs > 0 );
return $result;
} elsif( $cmd eq 'search' ) {
return "usage: search <keywords>" if( @args != 1 );
}
return "usage: search <keywords>" if( @args != 1 );
}
my $list = 'playlists:noArg channels:noArg channelscount:noArg inputs:noArg ls search';
return "Unknown argument $cmd, choose one of $list";
}
sub HEOSPlayer_Set($$@) {
my ($hash, $name, @aa) = @_;
my ($cmd, @args) = @aa;
my $pid = $hash->{PID};
@ -232,185 +272,270 @@ sub HEOSPlayer_Set($$@) {
my $rvalue;
my $favoritcount = 1;
my $qcount = 1;
my $string = "pid=$pid";
my $string = '';
#print "cmd ###################################################\n".Dumper($cmd);
if( $cmd eq 'getPlayerInfo' ) {
return "usage: getPlayerInfo" if( @args != 0 );
$heosCmd = $cmd;
} elsif( $cmd eq 'getPlayState' ) {
return "usage: getPlayState" if( @args != 0 );
$heosCmd = $cmd;
} elsif( $cmd eq 'getPlayMode' ) {
return "usage: getPlayMode" if( @args != 0 );
$heosCmd = $cmd;
} elsif( $cmd eq 'getNowPlayingMedia' ) {
return "usage: getNowPlayingMedia" if( @args != 0 );
$heosCmd = $cmd;
} elsif( $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');
$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');
$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 'mute' ) {
return "usage: mute on/off" if( @args != 1 );
$heosCmd = 'setMute';
$action = "state=$args[0]";
} elsif( $cmd eq 'volume' ) {
return "usage: volume 0-100" if( @args != 1 );
$heosCmd = 'setVolume';
$action = "level=$args[0]";
} elsif( $cmd eq 'volumeUp' ) {
return "usage: volumeUp 0-10" if( @args != 1 );
$heosCmd = $cmd;
$action = "step=$args[0]";
} elsif( $cmd eq 'volumeDown' ) {
return "usage: volumeDown 0-10" if( @args != 1 );
$heosCmd = $cmd;
$action = "step=$args[0]";
} elsif( $cmd eq 'groupWithMember' ) {
return "usage: groupWithMember" if( @args != 1 );
$pid .= ",$defs{$args[0]}->{PID}";
$heosCmd = 'createGroup';
} elsif( $cmd eq 'clearGroup' ) {
return "usage: clearGroup" if( @args != 0 );
$heosCmd = 'createGroup';
} 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 eq 'reReadSources' ) {
# return "usage: reReadSources" if( @args != 0 );
# $heosCmd = 'getMusicSources';
} elsif ( $cmd =~ /channel/ ) {
my $favorit = ReadingsVal($name,"channel", 0);
$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 );
$action = "preset=$args[0]";
} elsif( $cmd eq 'channelUp' ) {
return "usage: channelUp" if( @args != 0 );
$favorit = $favoritcount if ( ++$favorit > $favoritcount );
$action = "preset=".$favorit;
} elsif( $cmd eq 'channelDown' ) {
$favorit = 1 if ( --$favorit <= 0 );
$action = "preset=".$favorit;
}
} elsif ( $cmd =~ /Playlist/ ) {
my @cids = map { $_->{cid} } grep { $_->{name} =~ /$args[0]/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";
}
} else {
IOWrite($hash,'browseSource','sid=1025');
my @playlists = map { $_->{name} } (@{ $hash->{IODev}{helper}{playlists}});
return "usage: $cmd ".join(",",@playlists);
}
} elsif( $cmd eq 'input' ) {
my @sids;
my $search = $args[0];
$search =~ s/\xC2\xA0/ /g;
my $search = $args[0];
$search =~ s/\xC2\xA0/ /g;
#$search =~ s/\s+/\&nbsp;/g;
if ( $search =~ /Warteschlange/ ) {
push(@sids, "9999");
} else {
@sids = map { $_->{sid} } grep { $_->{name} =~ /\Q$search\E/i } (@{ $hash->{IODev}{helper}{sources} });
}
if ( scalar @args == 1 && scalar @sids > 0 ) {
readingsSingleUpdate($hash, "input", $args[0], 1);
#sid des Input für Container merken
readingsSingleUpdate($hash, ".input", $sids[0], 1);
#alten Container löschen bei Inputwechsel
readingsSingleUpdate($hash, ".cid", 0, 1);
if ( $sids[0] eq "9999" ) {
$heosCmd = 'getQueue';
} else {
} else {
$heosCmd = 'browseSource';
$action = "sid=$sids[0]";
}
Log3 $name, 4, "HEOSPlayer ($name) - set input with sid $sids[0] and name $args[0]";
} else {
my @inputs = map { $_->{name} } (@{ $hash->{IODev}{helper}{sources}});
push(@inputs, "Warteschlange");
return "usage: input ".join(",",@inputs);
}
} elsif( $cmd eq 'media' ) {
my @ids;
my $search = $args[0];
my $sid = ReadingsVal($name,".input", "9999");
my $search = $args[0];
my $sid = ReadingsVal($name,".input", "9999");
return "usage: set input first" unless( defined($sid) );
return "usage: set input first" unless( defined($sid) );
if ( scalar @args == 1 ) {
$search =~ s/\xC2\xA0/ /g;
$search =~ s/\xC2\xA0/ /g;
if ( $sid eq "9999" ) {
@ids = grep { $_->{song} =~ /\Q$search\E/i } (@{ $hash->{helper}{queue} });
} else {
@ids = grep { $_->{name} =~ /\Q$search\E/i } (@{ $hash->{IODev}{helper}{media} });
}
}
if ( scalar @ids > 0 ) {
if ( exists $ids[0]{cid} ) {
#hier Container verarbeiten
if ( $ids[0]{playable} eq "yes" ) {
#alles abspielen
$heosCmd = 'playPlaylist';
$action = "sid=$sid&cid=$ids[0]{cid}&aid=4";
#Container merken
readingsSingleUpdate($hash, ".cid", 0, 1);
} else {
#mehr einlesen
readingsSingleUpdate($hash, ".cid", $ids[0]{cid}, 1);
$heosCmd = 'browseSource';
$action = "sid=$sid&cid=$ids[0]{cid}";
}
} elsif ( exists $ids[0]{qid} ) {
$heosCmd = 'playQueue';
$action = "qid=$ids[0]{qid}";
} elsif ( exists $ids[0]{mid} ) {
#hier Medien verarbeiten
if ( $ids[0]{mid} =~ /inputs\// ) {
#Input abspielen
$heosCmd = 'playInput';
$action = "input=$ids[0]{mid}";
} else {
#aktuellen Container holen
my $cid = ReadingsVal($name,".cid", undef);
if ( defined $cid ) {
if ( $ids[0]{type} eq "station" ) {
#Radio abspielen
$heosCmd = 'playStream';
$action = "sid=$sid&cid=$cid&mid=$ids[0]{mid}";
} else {
#Song abspielen
$heosCmd = 'playPlaylist';
$action = "sid=$sid&cid=$cid&mid=$ids[0]{mid}&aid=4";
@ -419,63 +544,87 @@ sub HEOSPlayer_Set($$@) {
}
}
}
} else {
my @media;
if ( $sid eq "9999" ) {
@media = map { $_->{song} } (@{ $hash->{helper}{queue}});
} else {
@media = map { $_->{song} } (@{ $hash->{helper}{queue}});
} else {
@media = map { $_->{name} } (@{ $hash->{IODev}{helper}{media}});
}
return "usage: media ".join(",",@media);
}
} elsif ( $cmd eq 'clearQueue' ) {
#löscht die Warteschlange
return "usage: clearQueue" if( @args != 0 );
$heosCmd = 'clearQueue';
delete $hash->{helper}{queue};
} elsif ( $cmd eq 'saveQueue' ) {
#speichert die aktuelle Warteschlange als Playlist ab
return "usage: saveQueue" if( @args != 1 );
$heosCmd = 'saveQueue';
$action = "name=$args[0]";
} elsif ( $cmd eq 'history' ) {
$action = "name=$args[0]";
} elsif ( $cmd eq 'history' ) {
return "usage: history track,channel" if( @args != 1 );
$heosCmd = "browseSource";
$action = "sid=1026&cid=TRACKS" if ( $args[0] eq "track" );
$action = "sid=1026&cid=STATIONS" if ( $args[0] eq "channel" );
$action = "sid=1026&cid=STATIONS" if ( $args[0] eq "channel" );
} else {
my @playlists;
my @inputs;
my @media;
my @queue;
my $sid = ReadingsVal($name,".input", "9999");
my $sid = ReadingsVal($name,".input", "9999");
my $list = "getPlayerInfo:noArg getPlayState:noArg getNowPlayingMedia:noArg getPlayMode:noArg 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 channelUp:noArg channelDown:noArg next:noArg prev:noArg history:track,channel ";
$list .= "groupWithMember:" . join( ",", devspec2array("TYPE=HEOSPlayer:FILTER=NAME!=$name") );
#Parameterlisten für FHEMWeb zusammen bauen
$list .= " channel:slider,1,1,".scalar(@{$hash->{IODev}{helper}{favorites}}) if ( defined $hash->{IODev}{helper}{favorites} );
#$list .= " playQueue:slider,1,1,".scalar(@{$hash->{helper}{queue}}) if ( defined $hash->{helper}{queue} );
if ( defined $hash->{IODev}{helper}{playlists} ) {
@playlists = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{playlists}});
$list .= " playPlaylist:".join(",",@playlists) if( scalar @playlists > 0 );
$list .= " deletePlaylist:".join(",",@playlists) if( scalar @playlists > 0 );
#$list .= " renamePlaylist:".join(",",@playlists) if( scalar @playlists > 0 );
}
if ( defined $hash->{IODev}{helper}{sources}) {
@inputs = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{sources}});
push(@inputs, "Warteschlange");
$list .= " input:".join(",",@inputs) if( scalar @inputs > 0 );
}
}
if ( $sid eq "9999" ) {
@media = map { my %n; $n{name} = $_->{song}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{helper}{queue}});
$list .= " clearQueue:noArg saveQueue";
} else {
@media = map { my %n; $n{name} = $_->{name}; $n{name} =~ s/\s+/\&nbsp;/g; $n{name} } (@{ $hash->{IODev}{helper}{media}}) if ( defined $hash->{IODev}{helper}{media});
}
$list .= " media:".join(",",@media) if( scalar @media > 0 );
return "Unknown argument $cmd, choose one of $list";
}
$string .= "pid=$pid";
$string .= "&$action" if( defined($action));
IOWrite($hash,"$heosCmd","$string");
Log3 $name, 4, "HEOSPlayer ($name) - IOWrite: $heosCmd $string IODevHash=$hash->{IODev}";
@ -483,56 +632,82 @@ sub HEOSPlayer_Set($$@) {
}
sub HEOSPlayer_Parse($$) {
my ($io_hash,$json) = @_;
my $name = $io_hash->{NAME};
my $pid;
my $decode_json;
my $code;
my $code;
$decode_json = decode_json(encode_utf8($json));
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} ) {
if( my $hash = $modules{HEOSPlayer}{defptr}{$code} ) {
IOWrite($hash,'getPlayerInfo',"pid=$hash->{PID}");
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);
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( defined($decode_json->{payload}{pid}) );
Log3 $name, 4, "HEOSPlayer ($name) PID: $pid";
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( 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};
if ( $decode_json->{heos}{command} =~ /get_queue/ ) {
Log3 $name, 3, "HEOSPlayer ($name) - call getQueue for $message{pid}";
if ( defined $message{range} ) { $message{range} =~ s/(\d+)\,\d+/$1/; }
else { $message{range} = 0;
$hash->{helper}{queue} = [];
}
if ( $decode_json->{heos}{command} =~ /get_queue/ ) {
Log3 $name, 3, "HEOSPlayer ($name) - call getQueue for $message{pid}";
if ( defined $message{range} ) {
$message{range} =~ s/(\d+)\,\d+/$1/;
} else {
$message{range} = 0;
$hash->{helper}{queue} = [];
}
my $start = $message{range} + $message{returned};
push( @{$hash->{helper}{queue}}, (@{$decode_json->{payload}}) );
push( @{$hash->{helper}{queue}}, (@{$decode_json->{payload}}) );
if ( $start < $message{count} ) {
IOWrite($hash,'getQueue',"pid=$message{pid}&range=$start,".($start + 100));
Log3 $name, 3, "HEOSMaster ($name) - call getQueue with pid $message{pid} next Range from $message{returned}";
}
} else {
HEOSPlayer_WriteReadings($hash,$decode_json);
Log3 $name, 4, "HEOSPlayer ($name) - find logical device: $hash->{NAME}";
}
return $hash->{NAME};
} else {
my $devname = "HEOSPlayer".abs($pid);
return "UNDEFINED $devname HEOSPlayer $pid IODev=$name";
}
@ -540,9 +715,11 @@ sub HEOSPlayer_Parse($$) {
}
sub HEOSPlayer_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "HEOSPlayer ($name) - processing data to write readings";
############################
#### Aufbereiten der Daten soweit nötig (bei Events zum Beispiel)
@ -554,9 +731,11 @@ sub HEOSPlayer_WriteReadings($$) {
readingsBeginUpdate($hash);
### Event Readings
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 ) );
}
@ -587,6 +766,7 @@ sub HEOSPlayer_WriteReadings($$) {
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 );
readingsBulkUpdate( $hash, 'state', 'on' );
readingsEndUpdate( $hash, 1 );
@ -599,15 +779,20 @@ sub HEOSPlayer_WriteReadings($$) {
### my little Helpers
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};
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});
@ -617,76 +802,106 @@ sub HEOSPlayer_PreProcessingReadings($$) {
IOWrite($hash,'setPlayState',"pid=$hash->{PID}&state=play") if $buffer{'mute'} eq "off";
IOWrite($hash,'setPlayState',"pid=$hash->{PID}&state=stop") 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{'shuffle'} = $message{shuffle};
$buffer{'repeat'} = $message{repeat};
$buffer{'repeat'} ==~ s/.*\_(.*)/$1/g;
$buffer{'repeat'} ==~ s/.*\_(.*)/$1/g;
} 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}");
} elsif ( $decode_json->{heos}{command} =~ /play_preset/ ) {
$buffer{'channel'} = $message{preset}
$buffer{'channel'} = $message{preset}
} elsif ( $decode_json->{heos}{command} =~ /play_input/ ) {
$buffer{'input'} = $message{input};
$buffer{'input'} = $message{input};
} elsif ( $decode_json->{heos}{command} =~ /playback_error/ ) {
$buffer{'error'} = $message{error};
$buffer{'error'} = $message{error};
} else {
Log3 $name, 3, "HEOSPlayer ($name) - no match found";
return undef;
}
Log3 $name, 4, "HEOSPlayer ($name) - Match found for decode_json";
return \%buffer;
}
sub HEOSPlayer_GetPlayerInfo($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetPlayerInfo');
IOWrite($hash,'getPlayerInfo',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetPlayState($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetPlayState');
IOWrite($hash,'getPlayState',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetPlayMode($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetPlayMode');
IOWrite($hash,'getPlayMode',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetNowPlayingMedia($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetNowPlayingMedia');
IOWrite($hash,'getNowPlayingMedia',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetVolume($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetVolume');
IOWrite($hash,'getVolume',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetMute($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetMute');
IOWrite($hash,'getMute',"pid=$hash->{PID}");
}
sub HEOSPlayer_GetQueue($) {
my $hash = shift;
RemoveInternalTimer($hash,'HEOSPlayer_GetQueue');
IOWrite($hash,'getQueue',"pid=$hash->{PID}");
}