diverse set Befehle, aktivieren von Eventverarveitung, automatisches anlegen der Player, Fehlerbehandlung bei unsauberen JSON

This commit is contained in:
Marko Oldenburg 2017-01-26 14:53:29 +01:00
parent 2a0eb3a723
commit 18ef7f7fa9
2 changed files with 218 additions and 76 deletions

View File

@ -45,16 +45,19 @@ use JSON;
use Net::Telnet;
my $version = "0.1.17";
my $version = "0.1.28";
my %heosCmds = (
'enableEvents' => 'system/register_for_change_events?enable=',
'getPlayers' => 'player/get_players',
'getPlayerState' => 'player/get_player_info?',
'setPlayState' => 'player/set_play_state?',
'setMute' => 'player/set_mute?',
'setVolume' => 'player/set_volume?'
'enableChangeEvents' => 'system/register_for_change_events?enable=',
'getPlayers' => 'player/get_players',
'getPlayerInfo' => 'player/get_player_info?',
'getPlayerState' => 'player/get_player_state?',
'setPlayState' => 'player/set_play_state?',
'setMute' => 'player/set_mute?',
'setVolume' => 'player/set_volume?',
'getNowPlayingMedia' => 'player/get_now_playing_media?',
'eventChangeVolume' => 'event/player_volume_changed'
);
@ -73,6 +76,8 @@ sub HEOSMaster_Attr(@);
sub HEOSMaster_firstRun($);
sub HEOSMaster_ResponseProcessing($$);
sub HEOSMaster_WriteReadings($$);
sub HEOSMaster_GetPlayers($);
sub HEOSMaster_PreResponseProsessing($$);
@ -85,7 +90,7 @@ sub HEOSMaster_Initialize($) {
$hash->{ReadFn} = "HEOSMaster_Read";
$hash->{WriteFn} = "HEOSMaster_Write";
$hash->{Clients} = ":HEOSPlayer:";
$hash->{MatchList} = { "1:HEOSPlayer" => '.*{"command":."player.*' };
$hash->{MatchList} = { "1:HEOSPlayer" => '.*{"command":."player.*|.*{"command":."event\/player.*' };
# Consumer
@ -134,7 +139,8 @@ sub HEOSMaster_Define($$) {
if( $init_done ) {
InternalTimer( gettimeofday()+1, 'HEOSMaster_firstRun', $hash, 0 ) if( ($hash->{HOST}) );
HEOSMaster_firstRun($hash);
} else {
InternalTimer( gettimeofday()+15, 'HEOSMaster_firstRun', $hash, 0 ) if( ($hash->{HOST}) );
@ -197,6 +203,7 @@ sub HEOSMaster_Set($@) {
my ($arg, @params) = @args;
my $action;
my $heosCmd;
if($cmd eq 'reopen') {
return "usage: reopen" if( @args != 0 );
@ -209,29 +216,37 @@ sub HEOSMaster_Set($@) {
} elsif($cmd eq 'getPlayers') {
return "usage: getPlayers" if( @args != 0 );
$action = $cmd;
HEOSMaster_GetPlayers($hash);
} elsif($cmd eq 'send') {
return "usage: send" if( @args != 0 );
return undef;
} elsif($cmd eq 'enableChangeEvents') {
return "usage: enableChangeEvents" if( @args != 1 );
$heosCmd = $cmd;
$action = join(' ',@args);
} elsif($cmd eq 'eventSend') {
return "usage: enableChangeEvents" if( @args != 0 );
HEOSMaster_send($hash);
return undef;
return undef;
} else {
my $list = "";
$list .= "reopen:noArg getPlayers:noArg send:noArg";
$list .= "reopen:noArg getPlayers:noArg enableChangeEvents:on,off";
return "Unknown argument $cmd, choose one of $list";
}
HEOSMaster_Write($hash,$action,undef);
HEOSMaster_Write($hash,$heosCmd,$action);
}
sub HEOSMaster_send($) {
my $hash = shift;
HEOSMaster_Write($hash,'getPlayerState',"pid=-512565195");
HEOSMaster_Write($hash,'eventChangeVolume',undef);
}
sub HEOSMaster_firstRun($) {
@ -239,21 +254,22 @@ sub HEOSMaster_firstRun($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash);
#if( !IsDisabled($name) ) {
HEOSMaster_Open($hash) if( !IsDisabled($name) );
}
sub HEOSMaster_GetPlayers($) {
my $hash = shift;
my $name = $hash->{NAME};
HEOSMaster_Open($hash);
if( $hash->{CD} ) {
HEOSMaster_Write($hash,$heosCmds{enableEvents},'on');
Log3 $name, 3, "HEOSMaster ($name) - enable events at HEOS CLI";
} else {
Log3 $name, 3, "HEOSMaster ($name) - failed enable events at HEOS CLI";
}
#}
RemoveInternalTimer($hash);
HEOSMaster_Write($hash,'getPlayers',undef);
Log3 $name, 3, "HEOSMaster ($name) - getPlayers";
}
sub HEOSMaster_Open($) {
@ -281,6 +297,8 @@ sub HEOSMaster_Open($) {
readingsSingleUpdate($hash, 'state', 'connected', 1 );
Log3 $name, 3, "HEOSMaster ($name) - Socket Connected";
HEOSMaster_GetPlayers($hash);
}
sub HEOSMaster_Close($) {
@ -303,7 +321,7 @@ sub HEOSMaster_Write($@) {
my $name = $hash->{NAME};
my $string = "heos://$heosCmds{$heosCmd}";
$string .= "${value}" if(defined($value));
$string .= "${value}" if(defined($value) or $value ne '&');
$string .= "\r\n";
Log3 $name, 3, "HEOSMaster ($name) - WriteFn called";
@ -326,7 +344,7 @@ sub HEOSMaster_Read($) {
Log3 $name, 3, "HEOSMaster ($name) - ReadFn gestartet";
$len = sysread($hash->{CD},$buf,1024);
$len = sysread($hash->{CD},$buf,4096);
if( !defined($len) || !$len ) {
Log 1, "Länge? !!!!!!!!!!";
@ -335,13 +353,60 @@ sub HEOSMaster_Read($) {
unless( defined $buf) {
Log3 $name, 3, "HEOSMaster ($name) - Keine Daten empfangen";
return undef;
return;
}
if( $buf !~ m/^[\[{].*[}\]]$/ ) {
Log3 $name, 3, "HEOSMaster ($name) - invalid json detected. start preprocessing";
HEOSMaster_PreResponseProsessing($hash,$buf);
return;
}
Log3 $name, 3, "HEOSMaster ($name) - Daten: $buf";
HEOSMaster_ResponseProcessing($hash,$buf);
}
sub HEOSMaster_PreResponseProsessing($$) {
my ($hash,$response) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "HEOSMaster ($name) - pre processing respone data";
my $len = length($response);
my @letterArray = split("",$response);
my $letter = "";
my $count = 0;
my $marker = 0;
my $json;
for(my $i = 0; $i < $len; $i++) {
$marker = 1 if($count > 0);
$letter = $letterArray[0];
$json .= $letter;
$count++ if($letter eq '{');
$count-- if($letter eq '}');
if( $count == 0 and $marker == 1) {
HEOSMaster_ResponseProcessing($hash,$json);
$json = "";
$marker = 0;
}
shift(@letterArray);
}
#my $rest = join(' ',@letterArray); # currupted data, rest array
#Log3 $name, 3, "HEOSMaster ($name) - found corrupt data in buffer: $rest" if( defined($rest) and ($rest) );
}
sub HEOSMaster_ResponseProcessing($$) {
my ($hash,$json) = @_;
@ -349,28 +414,27 @@ sub HEOSMaster_ResponseProcessing($$) {
my $decode_json;
Log3 $name, 3, "HEOSMaster ($name) - JSON String: $json";
Log3 $name, 3, "HEOSMaster ($name) - JSON String: $json";
return Log3 $name, 3, "HEOSMaster ($name) - empty answer received"
unless( defined($json));
Log3 $name, 3, "HEOSMaster ($name) - json detected: $json";
$decode_json = decode_json($json);
return Log3 $name, 3, "HEOSMaster ($name) - decode_json has no Hash"
unless(ref($decode_json) eq "HASH");
if( defined($decode_json->{heos}{result}) and defined($decode_json->{heos}{command}) ) {
if( (defined($decode_json->{heos}{result}) and defined($decode_json->{heos}{command})) or ($decode_json->{heos}{command} =~ /^system/) ) {
HEOSMaster_WriteReadings($hash,$decode_json);
Log3 $name, 3, "HEOSMaster ($name) - call Sub HEOSMaster_WriteReadings";
}
if( $decode_json->{heos}{command} =~ /^player/ ) {
if( $decode_json->{heos}{command} =~ /^player/ or $decode_json->{heos}{command} =~ /^event\/player/ ) {
if( ref($decode_json->{payload}) eq "ARRAY" and scalar(@{$decode_json->{payload}}) > 0) {
foreach my $payload (@{$decode_json->{payload}}) {
@ -383,18 +447,23 @@ sub HEOSMaster_ResponseProcessing($$) {
Log3 $name, 3, "HEOSMaster ($name) - call Dispatcher";
}
return;
} elsif( defined($decode_json->{payload}{pid}) ) {
Dispatch($hash,$json,undef);
Log3 $name, 3, "HEOSMaster ($name) - call Dispatcher";
return;
} elsif( $decode_json->{heos}{message} =~ /^pid=/ ) {
Dispatch($hash,$json,undef);
Log3 $name, 3, "HEOSMaster ($name) - call Dispatcher";
return;
}
}
}
Log3 $name, 3, "HEOSMaster ($name) - no Match for processing data";
}
@ -403,9 +472,16 @@ sub HEOSMaster_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
my $value;
readingsBeginUpdate($hash);
if ( $decode_json->{heos}{command} =~ /register_for_change_events/ ) {
my @value = split('=', $decode_json->{heos}{message});
$value = $value[1];
readingsBulkUpdate( $hash, 'enableChangeEvents', "$value" );
}
readingsBulkUpdate( $hash, "lastCommand", $decode_json->{heos}{command} );
readingsBulkUpdate( $hash, "lastResult", $decode_json->{heos}{result} );

View File

@ -33,7 +33,7 @@ use warnings;
use JSON;
my $version = "0.1.17";
my $version = "0.1.28";
@ -47,6 +47,7 @@ sub HEOSPlayer_Parse($$);
sub HEOSPlayer_WriteReadings($$);
sub HEOSPlayer_Set($$@);
sub HEOSPlayer_GetUpdate($);
sub HEOSPlayer_PreProcessingReadings($$);
@ -55,7 +56,7 @@ sub HEOSPlayer_Initialize($) {
my ($hash) = @_;
$hash->{Match} = '.*{"command":."player.*';
$hash->{Match} = '.*{"command":."player.*|.*{"command":."event/player.*';
# Provider
$hash->{SetFn} = "HEOSPlayer_Set";
@ -134,7 +135,7 @@ sub HEOSPlayer_Define($$) {
if( $init_done ) {
HEOSPlayer_GetUpdate($hash);
} else {
} else {
InternalTimer( gettimeofday()+15, "HEOSPlayer_GetUpdate", $hash, 0 );
}
@ -153,7 +154,7 @@ sub HEOSPlayer_Undef($$) {
my $code = abs($pid);
$code = $hash->{IODev}->{NAME} ."-". $code if( defined($hash->{IODev}->{NAME}) );
Log3 $name, 3, "HEOSPlayer ($name) - undefined with Code: $code";
Log3 $name, 3, "HEOSPlayer ($name) - device deleted with Code: $code";
delete($modules{HEOSPlayer}{defptr}{$code});
return undef;
@ -194,16 +195,27 @@ sub HEOSPlayer_Set($$@) {
my ($hash, $name, @aa) = @_;
my ($cmd, @args) = @aa;
my $pid = $hash->{PID};
my $action;
my $heosCmd;
my $string = "pid=$pid";
if( $cmd eq 'statusRequest' ) {
return "usage: statusRequest" if( @args != 0 );
if( $cmd eq 'getPlayerInfo' ) {
return "usage: getPlayerInfo" if( @args != 0 );
HEOSPlayer_GetUpdate($hash);
return undef;
$heosCmd = 'getPlayerInfo';
} elsif( $cmd eq 'getPlayerState' ) {
return "usage: getPlayerState" if( @args != 0 );
$heosCmd = 'getPlayerState';
} elsif( $cmd eq 'getNowPlayingMedia' ) {
return "usage: getNowPlayingMedia" if( @args != 0 );
$heosCmd = 'getNowPlayingMedia';
} elsif( $cmd eq 'play' ) {
return "usage: play" if( @args != 0 );
@ -236,12 +248,16 @@ sub HEOSPlayer_Set($$@) {
$action = "level=$args[0]";
} else {
my $list = "statusRequest:noArg play:noArg stop:noArg pause:noArg mute:on,off volume:slider,0,5,100";
my $list = "getPlayerInfo:noArg getPlayerState:noArg play:noArg stop:noArg pause:noArg mute:on,off volume:slider,0,5,100";
return "Unknown argument $cmd, choose one of $list";
}
IOWrite($hash,"$heosCmd","pid=$hash->{PID}&$action");
Log3 $name, 3, "HEOSPlayer ($name) - IOWrite: pid=$hash->{PID}&state=$action IODevHash=$hash->{IODev}";
#IOWrite($hash,"$heosCmd","pid=$hash->{PID}&$action");
$string .= "&$action" if( defined($action));
IOWrite($hash,"$heosCmd","$string");
Log3 $name, 3, "HEOSPlayer ($name) - IOWrite: $heosCmd${string} IODevHash=$hash->{IODev}";
return undef;
}
@ -250,7 +266,11 @@ sub HEOSPlayer_GetUpdate($) {
my $hash = shift;
RemoveInternalTimer($hash);
IOWrite($hash,'getPlayerState',"pid=$hash->{PID}");
IOWrite($hash,'getPlayerInfo',"pid=$hash->{PID}");
return undef;
}
@ -291,8 +311,8 @@ sub HEOSPlayer_Parse($$) {
} else {
return Log3 $name, 3, "result not success"
unless($decode_json->{heos}{result} eq "success");
#return Log3 $name, 3, "result not success"
#unless($decode_json->{heos}{result} eq "success"); # Klappt bei Events nicht!! Lieber Fehlermeldung im Reading
if( defined($decode_json->{payload}{pid}) ) {
@ -328,30 +348,27 @@ sub HEOSPlayer_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
my $value;
Log3 $name, 3, "HEOSPlayer ($name) - write data to readings";
Log3 $name, 3, "HEOSPlayer ($name) - processing data to write readings";
############################
#### Status des Players
#### Aufbereiten der Daten soweit nötig (bei Events zum Beispiel)
my ($reading,$value) = HEOSPlayer_PreProcessingReadings($hash,$decode_json)
if( $decode_json->{heos}{message} =~ /^pid=/ );
############################
#### schreiben der Readings
readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, $reading, $value ) if( defined($reading) and defined($value));
if ( $decode_json->{heos}{command} =~ /set_play_state/ ) {
my @value = split('&', $decode_json->{heos}{message});
$value = substr($value[1],6);
readingsBulkUpdate( $hash, 'state', "$value" );
} elsif ( $decode_json->{heos}{command} =~ /set_volume/ ) {
my @value = split('&', $decode_json->{heos}{message});
$value = substr($value[1],6);
readingsBulkUpdate( $hash, 'volume', "$value" );
} else {
readingsBulkUpdate( $hash, 'state', 'Unknown' );
}
### PlayerInfos
readingsBulkUpdate( $hash, 'name', $decode_json->{payload}{name} );
readingsBulkUpdate( $hash, 'gid', $decode_json->{payload}{gid} );
readingsBulkUpdate( $hash, 'model', $decode_json->{payload}{model} );
@ -360,15 +377,64 @@ sub HEOSPlayer_WriteReadings($$) {
readingsBulkUpdate( $hash, 'lineout', $decode_json->{payload}{lineout} );
readingsBulkUpdate( $hash, 'control', $decode_json->{payload}{control} );
readingsBulkUpdate( $hash, 'ip-address', $decode_json->{payload}{ip} );
Log3 $name, 5, "HEOSPlayer ($name) - readings set for $name";
### playing Infos
readingsBulkUpdate( $hash, 'type', $decode_json->{payload}{type} );
readingsBulkUpdate( $hash, 'song', $decode_json->{payload}{song} );
readingsBulkUpdate( $hash, 'album', $decode_json->{payload}{album} );
readingsBulkUpdate( $hash, 'artist', $decode_json->{payload}{artist} );
readingsBulkUpdate( $hash, 'imageUrl', $decode_json->{payload}{image_url} );
readingsBulkUpdate( $hash, 'mid', $decode_json->{payload}{mid} );
readingsBulkUpdate( $hash, 'qid', $decode_json->{payload}{qid} );
readingsBulkUpdate( $hash, 'sid', $decode_json->{payload}{sid} );
readingsBulkUpdate( $hash, 'station', $decode_json->{payload}{station} );
readingsEndUpdate( $hash, 1 );
Log3 $name, 5, "HEOSPlayer ($name) - readings set for $name";
return undef;
}
###############
## little Helpers
sub HEOSPlayer_PreProcessingReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
my $reading;
my $value;
Log3 $name, 3, "HEOSPlayer ($name) - preprocessing readings";
if ( $decode_json->{heos}{command} =~ /play_state/ ) {
my @value = split('&', $decode_json->{heos}{message});
$value = substr($value[1],6);
$reading = 'state';
} elsif ( $decode_json->{heos}{command} =~ /set_volume/ ) {
my @value = split('&', $decode_json->{heos}{message});
$value = substr($value[1],6);
$reading = 'volume';
} elsif ( $decode_json->{heos}{command} =~ /volume_changed/ ) {
my @value = split('&', $decode_json->{heos}{message});
$value = substr($value[1],6);
$reading = 'volume';
} else {
Log3 $name, 3, "HEOSPlayer ($name) - no match found";
}
return($reading,$value);
}