2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-20 07:16:03 +00:00

70_XBMC: renamed pingInterval to updateInterval since it is now also used to update player status, receive OnSeek/OnSpeedChange/onPropertyChange and generate readings

git-svn-id: https://svn.fhem.de/fhem/trunk@8627 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
vbs2 2015-05-24 14:47:04 +00:00
parent 88e3f85001
commit 78175aec48

View File

@ -29,7 +29,7 @@ sub XBMC_Initialize($$)
$hash->{ReadFn} = "XBMC_Read"; $hash->{ReadFn} = "XBMC_Read";
$hash->{ReadyFn} = "XBMC_Ready"; $hash->{ReadyFn} = "XBMC_Ready";
$hash->{UndefFn} = "XBMC_Undefine"; $hash->{UndefFn} = "XBMC_Undefine";
$hash->{AttrList} = "fork:enable,disable compatibilityMode:xbmc,plex offMode:quit,hibernate,shutdown,standby pingInterval " . $readingFnAttributes; $hash->{AttrList} = "fork:enable,disable compatibilityMode:xbmc,plex offMode:quit,hibernate,shutdown,standby updateInterval " . $readingFnAttributes;
$data{RC_makenotify}{XBMC} = "XBMC_RCmakenotify"; $data{RC_makenotify}{XBMC} = "XBMC_RCmakenotify";
$data{RC_layout}{XBMC_RClayout} = "XBMC_RClayout"; $data{RC_layout}{XBMC_RClayout} = "XBMC_RClayout";
@ -70,7 +70,7 @@ sub XBMC_Define($$)
return "Username and/or password missing."; return "Username and/or password missing.";
} }
$attr{$hash->{NAME}}{"pingInterval"} = 60; $attr{$hash->{NAME}}{"updateInterval"} = 60;
return undef; return undef;
} }
@ -171,22 +171,49 @@ sub XBMC_Init($)
XBMC_Update($hash); XBMC_Update($hash);
XBMC_QueueCheckConnection($hash); XBMC_QueueIntervalUpdate($hash);
return undef; return undef;
} }
sub XBMC_QueueCheckConnection($;$) { sub XBMC_QueueIntervalUpdate($;$) {
my ($hash, $time) = @_; my ($hash, $time) = @_;
# AFAIK when using http this module is not using a persistent TCP connection # AFAIK when using http this module is not using a persistent TCP connection
if($hash->{Protocol} ne 'http') { if($hash->{Protocol} ne 'http') {
if (!defined($time)) { if (!defined($time)) {
$time = AttrVal($hash->{NAME},'pingInterval','60'); $time = AttrVal($hash->{NAME},'updateInterval','60');
} }
InternalTimer(time() + $time, "XBMC_CheckConnection", $hash, 0); InternalTimer(time() + $time, "XBMC_Check", $hash, 0);
} }
} }
sub XBMC_Check($) {
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "XBMC_Check";
XBMC_CheckConnection($hash);
XBMC_Update($hash);
#xbmc seems alive. so keep bugging it
XBMC_QueueIntervalUpdate($hash);
}
sub XBMC_UpdatePlayerItem($) {
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 4, "XBMC_UpdatePlayerItem";
if (($hash->{STATE} eq 'disconnected') or (ReadingsVal($name, "playStatus","") ne 'playing')) {
Log3 $name, 4, "XBMC_UpdatePlayerItem - cancelled";
return;
}
XBMC_PlayerGetItem($hash, -1);
}
sub XBMC_CheckConnection($) { sub XBMC_CheckConnection($) {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
@ -215,9 +242,6 @@ sub XBMC_CheckConnection($) {
$hash->{LAST_PING} = time(); $hash->{LAST_PING} = time();
DevIo_SimpleWrite($hash, $json, 0); DevIo_SimpleWrite($hash, $json, 0);
#xbmc seems alive. so keep bugging it
XBMC_QueueCheckConnection($hash);
} }
sub XBMC_Update($) sub XBMC_Update($)
@ -240,6 +264,8 @@ sub XBMC_Update($)
}; };
XBMC_Call($hash,$obj,1); XBMC_Call($hash,$obj,1);
XBMC_PlayerUpdate($hash,-1); #-1 -> update all existing players XBMC_PlayerUpdate($hash,-1); #-1 -> update all existing players
XBMC_UpdatePlayerItem($hash);
} }
sub XBMC_PlayerUpdate($$) sub XBMC_PlayerUpdate($$)
@ -263,6 +289,26 @@ sub XBMC_PlayerUpdate($$)
} }
} }
sub XBMC_PlayerGetItem($$)
{
my $hash = shift;
my $playerid = shift;
my $obj = {
"method" => "Player.GetItem",
"params" => {
"properties" => ["artist", "album", "thumbnail", "file", "title",
"track", "year", "streamdetails"]
}
};
if($playerid >= 0) {
$obj->{params}->{playerid} = $playerid;
XBMC_Call($hash,$obj,1);
}
else {
XBMC_PlayerCommand($hash,$obj,0);
}
}
sub XBMC_Read($) sub XBMC_Read($)
{ {
my ($hash) = @_; my ($hash) = @_;
@ -290,7 +336,7 @@ sub XBMC_ProcessRead($$)
Log3($name, 5, "XBMC_Read: Incoming data: " . $data); Log3($name, 5, "XBMC_Read: Incoming data: " . $data);
$buffer = $buffer . $data; $buffer = $buffer . $data;
Log3($name, 4, "XBMC_Read: Current processing buffer (PARTIAL + incoming data): " . $buffer); Log3($name, 5, "XBMC_Read: Current processing buffer (PARTIAL + incoming data): " . $buffer);
my ($msg,$tail) = XBMC_ParseMsg($hash, $buffer); my ($msg,$tail) = XBMC_ParseMsg($hash, $buffer);
#processes all complete messages #processes all complete messages
@ -367,9 +413,9 @@ sub XBMC_PlayerOnPlay($$)
my ($hash,$obj) = @_; my ($hash,$obj) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $id = XBMC_CreateId(); my $id = XBMC_CreateId();
my $playerId;
my $type = $obj->{params}->{data}->{item}->{type}; my $type = $obj->{params}->{data}->{item}->{type};
if(AttrVal($hash->{NAME},'compatibilityMode','xbmc') eq 'plex' || !defined($obj->{params}->{data}->{item}->{id}) || $type eq "picture" || $type eq "unknown") { if(AttrVal($hash->{NAME},'compatibilityMode','xbmc') eq 'plex' || !defined($obj->{params}->{data}->{item}->{id}) || $type eq "picture" || $type eq "unknown") {
# we either got unknown or picture OR an item not in the library (id not existing)
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash,'playStatus','playing'); readingsBulkUpdate($hash,'playStatus','playing');
readingsBulkUpdate($hash,'type',$type); readingsBulkUpdate($hash,'type',$type);
@ -381,14 +427,10 @@ sub XBMC_PlayerOnPlay($$)
} }
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
if ($type eq "unknown") { XBMC_PlayerGetItem($hash, -1);
# this is special. we get here for example when playing a stream
# xbmc is not able to assign the correct player so the playerid might be wrong
# http://forum.kodi.tv/showthread.php?tid=174872
$playerId = -1; #signal that we are unsure about the playerId and that we want to call Player.GetActivePlayers first
}
} }
elsif($type eq "song") { elsif($type eq "song") {
#
my $req = { my $req = {
"method" => "AudioLibrary.GetSongDetails", "method" => "AudioLibrary.GetSongDetails",
"params" => { "params" => {
@ -460,12 +502,11 @@ sub XBMC_PlayerOnPlay($$)
XBMC_Call($hash, $req,1); XBMC_Call($hash, $req,1);
} }
if (not defined($playerId)) { # the playerId in the message is not reliable
# this happens when we did not had a type 'unknown' # xbmc is not able to assign the correct player so the playerid might be wrong
# so basically always :) # http://forum.kodi.tv/showthread.php?tid=174872
$playerId = $obj->{params}->{data}->{player}->{playerid}; # so we ask for the acutally running players by passing -1
} XBMC_PlayerUpdate($hash, -1);
XBMC_PlayerUpdate($hash, $playerId);
} }
sub XBMC_ProcessNotification($$) sub XBMC_ProcessNotification($$)
@ -486,13 +527,14 @@ sub XBMC_ProcessNotification($$)
elsif($obj->{method} eq "Player.OnPropertyChanged") { elsif($obj->{method} eq "Player.OnPropertyChanged") {
XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid});
} }
elsif($obj->{method} eq "Player.OnSeek") { elsif($obj->{method} =~ /(Player\.OnSeek|Player\.OnSpeedChanged|Player\.OnPropertyChanged)/) {
#XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); my $base = $obj->{params}->{data}->{player};
Log3($name, 4, "Discard Player.OnSeek event because it is irrelevant"); readingsBeginUpdate($hash);
foreach my $key (keys %$base) {
my $item = $base->{$key};
XBMC_CreateReading($hash,$key,$item);
} }
elsif($obj->{method} eq "Player.OnSpeedChanged") { readingsEndUpdate($hash, 1);
#XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid});
Log3($name, 4, "Discard Player.OnSpeedChanged event because it is irrelevant");
} }
elsif($obj->{method} eq "Player.OnStop") { elsif($obj->{method} eq "Player.OnStop") {
readingsSingleUpdate($hash,"playStatus",'stopped',1); readingsSingleUpdate($hash,"playStatus",'stopped',1);
@ -504,7 +546,7 @@ sub XBMC_ProcessNotification($$)
XBMC_ResetMediaReadings($hash); XBMC_ResetMediaReadings($hash);
XBMC_PlayerOnPlay($hash, $obj); XBMC_PlayerOnPlay($hash, $obj);
} }
elsif($obj->{method} =~ /(.*).On(.*)/) { elsif($obj->{method} =~ /(Playlist|AudioLibrary|VideoLibrary|System).On(.*)/) {
readingsSingleUpdate($hash,lc($1),lc($2),1); readingsSingleUpdate($hash,lc($1),lc($2),1);
if (lc($1) eq "system") { if (lc($1) eq "system") {
@ -520,7 +562,7 @@ sub XBMC_ProcessNotification($$)
#and force a connection check in some seconds when we think XBMC actually has shut down #and force a connection check in some seconds when we think XBMC actually has shut down
$hash->{LAST_PONG} = 0; $hash->{LAST_PONG} = 0;
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
XBMC_QueueCheckConnection($hash, 5); XBMC_QueueIntervalUpdate($hash, 5);
} }
} }
} }
@ -565,14 +607,23 @@ sub XBMC_ProcessResponse($$)
$hash->{PendingPlayerCMDs}{$id} = undef; $hash->{PendingPlayerCMDs}{$id} = undef;
} }
else { else {
my $properties = $obj->{result}; my $result = $obj->{result};
if($properties && $properties ne 'OK') { if($result && $result ne 'OK') {
if ($properties ne 'pong') { if ($result ne 'pong') {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
foreach my $key (keys %$properties) { foreach my $key (keys %$result) {
my $value = $properties->{$key}; if ($key eq 'item') {
my $item = $obj->{result}->{item};
foreach my $ikey (keys %$item) {
my $value = $item->{$ikey};
XBMC_CreateReading($hash,$ikey,$value);
}
}
else {
my $value = $result->{$key};
XBMC_CreateReading($hash,$key,$value); XBMC_CreateReading($hash,$key,$value);
} }
}
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
} }
else { else {
@ -593,8 +644,12 @@ sub XBMC_Is3DFile($$) {
sub XBMC_CreateReading($$$); sub XBMC_CreateReading($$$);
sub XBMC_CreateReading($$$) { sub XBMC_CreateReading($$$) {
my $hash = shift; my $hash = shift;
my $name = $hash->{NAME};
my $key = shift; my $key = shift;
my $value = shift; my $value = shift;
return if ($key =~ /(playerid)/);
if($key eq 'version') { if($key eq 'version') {
my $version = ''; my $version = '';
$version = $value->{major}; $version = $value->{major};
@ -606,7 +661,7 @@ sub XBMC_CreateReading($$$) {
elsif($key eq 'skin') { elsif($key eq 'skin') {
$value = $value->{name} . '(' . $value->{id} . ')'; $value = $value->{name} . '(' . $value->{id} . ')';
} }
elsif($key eq 'totaltime' || $key eq 'time') { elsif($key =~ /(totaltime|time|seekoffset)/) {
$value = sprintf('%02d:%02d:%02d.%03d',$value->{hours},$value->{minutes},$value->{seconds},$value->{milliseconds}); $value = sprintf('%02d:%02d:%02d.%03d',$value->{hours},$value->{minutes},$value->{seconds},$value->{milliseconds});
} }
elsif($key eq 'shuffled') { elsif($key eq 'shuffled') {
@ -630,6 +685,7 @@ sub XBMC_CreateReading($$$) {
readingsBulkUpdate($hash,'3dfile', XBMC_Is3DFile($hash, $value) ? "on" : "off"); readingsBulkUpdate($hash,'3dfile', XBMC_Is3DFile($hash, $value) ? "on" : "off");
} }
elsif($key =~ /(album|artist|track|title)/) { elsif($key =~ /(album|artist|track|title)/) {
$value = "" if $value eq -1;
$key = 'current' . ucfirst($key); $key = 'current' . ucfirst($key);
} }
elsif($key eq 'streamdetails') { elsif($key eq 'streamdetails') {
@ -647,11 +703,18 @@ sub XBMC_CreateReading($$$) {
$key = undef; $key = undef;
} }
if(ref($value) eq 'ARRAY') { if(ref($value) eq 'ARRAY') {
if(int(@$value)) {
$value = join(',',@$value); $value = join(',',@$value);
} }
if (defined $key) {
if ($key =~ /(seekoffset)/) {
# for these readings we do only events - no readings
DoTrigger($name, "$key: $value");
}
else {
readingsBulkUpdate($hash,$key,$value) ;
}
} }
readingsBulkUpdate($hash,$key,$value) if defined $key;
} }
#Parses a given string and returns ($msg,$tail). If the string contains a complete message #Parses a given string and returns ($msg,$tail). If the string contains a complete message
@ -1165,7 +1228,7 @@ sub XBMC_Call($$$)
} }
$obj->{jsonrpc} = "2.0"; #JSON RPC version has to be passed $obj->{jsonrpc} = "2.0"; #JSON RPC version has to be passed
my $json = JSON->new->utf8(0)->encode($obj); my $json = JSON->new->utf8(0)->encode($obj);
Log3($name, 5, "XBMC_Call: Sending: " . $json); Log3($name, 4, "XBMC_Call: Sending: " . $json);
if($hash->{Protocol} eq 'http') { if($hash->{Protocol} eq 'http') {
return XBMC_HTTP_Call($hash,$json,$id); return XBMC_HTTP_Call($hash,$json,$id);
} }
@ -1534,6 +1597,8 @@ sub XBMC_HTTP_Request($$@)
If XBMC does not run all the time it used to be the case that FHEM blocks because it cannot reach XBMC (only happened If XBMC does not run all the time it used to be the case that FHEM blocks because it cannot reach XBMC (only happened
if TCP was used). If you encounter problems like FHEM not responding for a few seconds then you should set <code>attr &lt;XBMC_device&gt; fork enable</code> if TCP was used). If you encounter problems like FHEM not responding for a few seconds then you should set <code>attr &lt;XBMC_device&gt; fork enable</code>
which will move the search for XBMC into a separate process.</li> which will move the search for XBMC into a separate process.</li>
<li>updateInterval<br>
The interval which is used to check if Kodi is still alive (by sending a JSON ping) and also it is used to update current player item.</li>
</ul> </ul>
</ul> </ul>