diff --git a/fhem/FHEM/70_XBMC.pm b/fhem/FHEM/70_XBMC.pm index 8a49ab83c..7c7223a32 100644 --- a/fhem/FHEM/70_XBMC.pm +++ b/fhem/FHEM/70_XBMC.pm @@ -29,7 +29,7 @@ sub XBMC_Initialize($$) $hash->{ReadFn} = "XBMC_Read"; $hash->{ReadyFn} = "XBMC_Ready"; $hash->{UndefFn} = "XBMC_Undefine"; - $hash->{AttrList} = "fork:enable,disable compatibilityMode:xbmc,plex offMode:quit,hibernate,shutdown,standby " . $readingFnAttributes; + $hash->{AttrList} = "fork:enable,disable compatibilityMode:xbmc,plex offMode:quit,hibernate,shutdown,standby pingInterval " . $readingFnAttributes; $data{RC_makenotify}{XBMC} = "XBMC_RCmakenotify"; $data{RC_layout}{XBMC_RClayout} = "XBMC_RClayout"; @@ -60,7 +60,7 @@ sub XBMC_Define($$) if($protocol eq 'tcp') { $hash->{DeviceName} = $hash->{Host} . ":" . $hash->{Port}; my $dev = $hash->{DeviceName}; - $readyfnlist{"$name.$dev"} = $hash; + $readyfnlist{"$name.$dev"} = $hash; } elsif(defined($username) && defined($password)) { $hash->{Username} = $username; @@ -69,6 +69,9 @@ sub XBMC_Define($$) else { return "Username and/or password missing."; } + + $attr{$hash->{NAME}}{"pingInterval"} = 60; + return undef; } @@ -83,12 +86,26 @@ sub XBMC_Ready($) } elsif(!$hash->{CHILDPID}) { return if($hash->{CHILDPID} = fork); - my $ppid = getppid(); - while(kill 0, $ppid) { - DevIo_OpenDev($hash, 1, "XBMC_ChildExit"); - sleep(5); - } - exit(0); + my $ppid = getppid(); + + ### Copied from Blocking.pm + foreach my $d (sort keys %defs) { # Close all kind of FD + my $h = $defs{$d}; + #the following line was added by vbs to not close parent's DbLog DB handle + $h->{DBH}->{InactiveDestroy} = 1 if ($h->{TYPE} eq 'DbLog'); + TcpServer_Close($h) if($h->{SERVERSOCKET}); + if($h->{DeviceName}) { + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + DevIo_CloseDev($h,1); + } + } + ### End of copied from Blocking.pm + + while(kill 0, $ppid) { + DevIo_OpenDev($hash, 1, "XBMC_ChildExit"); + sleep(5); + } + exit(0); } } else { return DevIo_OpenDev($hash, 1, "XBMC_Init"); @@ -105,6 +122,9 @@ sub XBMC_ChildExit($) sub XBMC_Undefine($$) { my ($hash,$arg) = @_; + + RemoveInternalTimer($hash); + if($hash->{Protocol} eq 'tcp') { DevIo_CloseDev($hash); } @@ -114,10 +134,62 @@ sub XBMC_Undefine($$) sub XBMC_Init($) { my ($hash) = @_; + + #since we just successfully connected to XBMC I guess its safe to assume the device is awake + readingsSingleUpdate($hash,"system","wake",1); + $hash->{LAST_PING} = $hash->{LAST_PONG} = time(); + XBMC_Update($hash); + + XBMC_QueueCheckConnection($hash); + return undef; } +sub XBMC_QueueCheckConnection($;$) { + my ($hash, $time) = @_; + # AFAIK when using http this module is not using a persistent TCP connection + if($hash->{Protocol} ne 'http') { + if (!defined($time)) { + $time = AttrVal($hash->{NAME},'pingInterval','60'); + } + InternalTimer(time() + $time, "XBMC_CheckConnection", $hash, 0); + } +} + +sub XBMC_CheckConnection($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + if ($hash->{STATE} eq "disconnected") { + # we are already disconnected + return; + } + + #do not call XBMC_CheckConnection a second time before the pong had a chance to arrive + #otherwise the connection will be considered as lost + if ($hash->{LAST_PING} > $hash->{LAST_PONG}) { + Log3 $name, 3, "Last ping (" . $hash->{LAST_PING} . ") is greather than last pong (" . $hash->{LAST_PONG} . ")"; + DevIo_Disconnected($hash); + return; + } + + my $obj = { + "method" => "JSONRPC.Ping", + }; + $obj->{id} = XBMC_CreateId(); + $obj->{jsonrpc} = "2.0"; #JSON RPC version has to be passed + + # remember: we only get here when using TCP (not HTTP) + my $json = encode_json($obj); + + $hash->{LAST_PING} = time(); + DevIo_SimpleWrite($hash, $json, 0); + + #xbmc seems alive. so keep bugging it + XBMC_QueueCheckConnection($hash); +} + sub XBMC_Update($) { my ($hash) = @_; @@ -143,7 +215,7 @@ sub XBMC_Update($) } }; XBMC_Call($hash,$obj,1); - XBMC_PlayerUpdate($hash,0); + XBMC_PlayerUpdate($hash,-1); #-1 -> update all existing players } sub XBMC_PlayerUpdate($$) @@ -154,12 +226,12 @@ sub XBMC_PlayerUpdate($$) "method" => "Player.GetProperties", "params" => { "properties" => ["time","totaltime", "repeat", "shuffled", "speed" ] - #"canseek", "canchangespeed", "canmove", "canzoom", "canrotate", "canshuffle", "canrepeat" + #"canseek", "canchangespeed", "canmove", "canzoom", "canrotate", "canshuffle", "canrepeat" } }; push(@{$obj->{params}->{properties}}, 'partymode') if(AttrVal($hash->{NAME},'compatibilityMode','xbmc') eq 'xbmc'); if($playerid >= 0) { - $obj->{params}->{playerid} = $playerid; + $obj->{params}->{playerid} = $playerid; XBMC_Call($hash,$obj,1); } else { @@ -170,42 +242,156 @@ sub XBMC_PlayerUpdate($$) sub XBMC_Read($) { my ($hash) = @_; + my $buffer = DevIo_SimpleRead($hash); + return XBMC_ProcessRead($hash, $buffer); +} + +sub XBMC_ProcessRead($$) +{ + my ($hash, $data) = @_; + my $name = $hash->{NAME}; my $buffer = ''; + Log3($name, 5, "XBMC_ProcessRead"); + #include previous partial message if(defined($hash->{PARTIAL}) && $hash->{PARTIAL}) { - $buffer = $hash->{PARTIAL} . DevIo_SimpleRead($hash); + Log3($name, 5, "XBMC_Read: PARTIAL: " . $hash->{PARTIAL}); + $buffer = $hash->{PARTIAL}; } else { - $buffer = DevIo_SimpleRead($hash); + Log3($name, 5, "No PARTIAL buffer"); } + + Log3($name, 5, "XBMC_Read: Incoming data: " . $data); + + $buffer = $buffer . $data; + Log3($name, 5, "XBMC_Read: Current processing buffer (PARTIAL + incoming data): " . $buffer); + my ($msg,$tail) = XBMC_ParseMsg($buffer); #processes all complete messages while($msg) { - my $obj = decode_json($msg); - Log 5, "XBMC received message:" . $msg; - #it is a notification if a method name is present + Log3($name, 5, "XBMC_Read: Decoding JSON message. Length: " . length($msg) . " Content: " . $msg); + my $obj = JSON->new->utf8(0)->decode($msg); + #it is a notification if a method name is present if(defined($obj->{method})) { XBMC_ProcessNotification($hash,$obj); } - #otherwise it is a answer of a request + elsif(defined($obj->{error})) { + Log3($name, 3, "XBMC_Read: Received error message: " . $msg); + } + #otherwise it is a answer of a request else { XBMC_ProcessResponse($hash,$obj); } ($msg,$tail) = XBMC_ParseMsg($tail); } $hash->{PARTIAL} = $tail; - Log 5, "Tail:" . $tail; + Log3($name, 5, "XBMC_Read: Tail: " . $tail); + Log3($name, 5, "XBMC_Read: PARTIAL: " . $hash->{PARTIAL}); +} + +sub XBMC_PlayerOnPlay($$) +{ + my ($hash,$obj) = @_; + my $name = $hash->{NAME}; + my $id = XBMC_CreateId(); + 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") { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,'playStatus','playing'); + readingsBulkUpdate($hash,'type',$type); + if(defined($obj->{params}->{data}->{item})) { + foreach my $key (keys %{$obj->{params}->{data}->{item}}) { + my $value = $obj->{params}->{data}->{item}->{$key}; + XBMC_CreateReading($hash,$key,$value); + } + } + readingsEndUpdate($hash, 1); + } + elsif($type eq "song") { + my $req = { + "method" => "AudioLibrary.GetSongDetails", + "params" => { + "songid" => $obj->{params}->{data}->{item}->{id}, + "properties" => ["artist","album","title","track","file"] + }, + "id" => $id + }; + my $event = { + "name" => $obj->{method}, + "type" => "song", + "event" => 'Player.OnPlay' + }; + $hash->{PendingEvents}{$id} = $event; + XBMC_Call($hash, $req,1); + } + elsif($type eq "episode") { + my $req = { + "method" => "VideoLibrary.GetEpisodeDetails", + "params" => { + "episodeid" => $obj->{params}->{data}->{item}->{id}, + #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.Episode + "properties" => ["season","episode","title","showtitle","file"] + }, + "id" => $id + }; + my $event = { + "name" => $obj->{method}, + "type" => "episode", + "event" => 'Player.OnPlay' + }; + $hash->{PendingEvents}{$id} = $event; + XBMC_Call($hash, $req,1); + } + elsif($type eq "movie") { + my $req = { + "method" => "VideoLibrary.GetMovieDetails", + "params" => { + "movieid" => $obj->{params}->{data}->{item}->{id}, + #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.Movie + "properties" => ["title","file","year","originaltitle"] + }, + "id" => $id + }; + my $event = { + "name" => $obj->{method}, + "type" => "movie", + "event" => 'Player.OnPlay' + }; + $hash->{PendingEvents}{$id} = $event; + XBMC_Call($hash, $req,1); + } + elsif($type eq "musicvideo") { + my $req = { + "method" => "VideoLibrary.GetMusicVideoDetails", + "params" => { + "musicvideoid" => $obj->{params}->{data}->{item}->{id}, + #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.MusicVideo + "properties" => ["title","artist","album","file"] + }, + "id" => $id + }; + my $event = { + "name" => $obj->{method}, + "type" => "musicvideo", + "event" => 'Player.OnPlay' + }; + $hash->{PendingEvents}{$id} = $event; + XBMC_Call($hash, $req,1); + } + XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); } sub XBMC_ProcessNotification($$) { my ($hash,$obj) = @_; + my $name = $hash->{NAME}; #React on volume change - http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Application.OnVolumeChanged if($obj->{method} eq "Application.OnVolumeChanged") { readingsBeginUpdate($hash); - readingsBulkUpdate($hash,'volume',$obj->{params}->{data}->{volume}); - readingsBulkUpdate($hash,'mute',($obj->{params}->{data}->{muted} ? 'on' : 'off')); - readingsEndUpdate($hash, 1); + readingsBulkUpdate($hash,'volume',$obj->{params}->{data}->{volume}); + readingsBulkUpdate($hash,'mute',($obj->{params}->{data}->{muted} ? 'on' : 'off')); + readingsEndUpdate($hash, 1); } #React on play, pause and stop #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Player.OnPlay @@ -216,11 +402,11 @@ sub XBMC_ProcessNotification($$) } elsif($obj->{method} eq "Player.OnSeek") { #XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); - Log 3, "Discard Player.OnSeek event because it is irrelevant"; + Log3($name, 4, "Discard Player.OnSeek event because it is irrelevant"); } elsif($obj->{method} eq "Player.OnSpeedChanged") { #XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); - Log 3, "Discard Player.OnSpeedChanged event because it is irrelevant"; + Log3($name, 3, "Discard Player.OnSpeedChanged event because it is irrelevant"); } elsif($obj->{method} eq "Player.OnStop") { readingsSingleUpdate($hash,"playStatus",'stopped',1); @@ -229,95 +415,21 @@ sub XBMC_ProcessNotification($$) readingsSingleUpdate($hash,"playStatus",'paused',1); } elsif($obj->{method} eq "Player.OnPlay") { - my $id = XBMC_CreateId(); - 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") { - readingsBeginUpdate($hash); - readingsBulkUpdate($hash,'playStatus','playing'); - readingsBulkUpdate($hash,'type',$type); - if(defined($obj->{params}->{data}->{item})) { - foreach my $key (keys %{$obj->{params}->{data}->{item}}) { - my $value = $obj->{params}->{data}->{item}->{$key}; - XBMC_CreateReading($hash,$key,$value); - } - } - readingsEndUpdate($hash, 1); - } - elsif($type eq "song") { - my $req = { - "method" => "AudioLibrary.GetSongDetails", - "params" => { - "songid" => $obj->{params}->{data}->{item}->{id}, - "properties" => ["artist","album","title","track","file"] - }, - "id" => $id - }; - my $event = { - "name" => $obj->{method}, - "type" => "song", - "event" => 'Player.OnPlay' - }; - $hash->{PendingEvents}{$id} = $event; - XBMC_Call($hash, $req,1); - } - elsif($type eq "episode") { - my $req = { - "method" => "VideoLibrary.GetEpisodeDetails", - "params" => { - "episodeid" => $obj->{params}->{data}->{item}->{id}, - #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.Episode - "properties" => ["season","episode","title","showtitle","file"] - }, - "id" => $id - }; - my $event = { - "name" => $obj->{method}, - "type" => "episode", - "event" => 'Player.OnPlay' - }; - $hash->{PendingEvents}{$id} = $event; - XBMC_Call($hash, $req,1); - } - elsif($type eq "movie") { - my $req = { - "method" => "VideoLibrary.GetMovieDetails", - "params" => { - "movieid" => $obj->{params}->{data}->{item}->{id}, - #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.Movie - "properties" => ["title","file","year","originaltitle"] - }, - "id" => $id - }; - my $event = { - "name" => $obj->{method}, - "type" => "movie", - "event" => 'Player.OnPlay' - }; - $hash->{PendingEvents}{$id} = $event; - XBMC_Call($hash, $req,1); - } - elsif($type eq "musicvideo") { - my $req = { - "method" => "VideoLibrary.GetMusicVideoDetails", - "params" => { - "musicvideoid" => $obj->{params}->{data}->{item}->{id}, - #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Video.Fields.MusicVideo - "properties" => ["title","artist","album","file"] - }, - "id" => $id - }; - my $event = { - "name" => $obj->{method}, - "type" => "musicvideo", - "event" => 'Player.OnPlay' - }; - $hash->{PendingEvents}{$id} = $event; - XBMC_Call($hash, $req,1); - } - XBMC_PlayerUpdate($hash,$obj->{params}->{data}->{player}->{playerid}); + XBMC_PlayerOnPlay($hash, $obj); } elsif($obj->{method} =~ /(.*).On(.*)/) { readingsSingleUpdate($hash,lc($1),lc($2),1); + + if ((lc($1) eq "system") and (lc($2) eq "sleep")) { + Log3($name, 3, "XBMC notified that it is going to sleep"); + #if we immediatlely close our DevIO then fhem will instantly try to reconnect which might + #succeed because XBMC needs a moment to actually shutdown. + #So cancel the current timer, fake that the last pong has arrived ages ago + #and force a connection check in some seconds when we think XBMC actually has shut down + $hash->{LAST_PONG} = 0; + RemoveInternalTimer($hash); + XBMC_QueueCheckConnection($hash, 5); + } } return undef; } @@ -332,43 +444,49 @@ sub XBMC_ProcessResponse($$) my $name = $event->{name}; my $type = $event->{type}; my $value = ''; - my $base = ''; + my $base = ''; $base = $obj->{result}->{songdetails} if($type eq 'song'); - $base = $obj->{result}->{episodedetails} if($type eq 'episode'); - $base = $obj->{result}->{moviedetails} if($type eq 'movie'); - $base = $obj->{result}->{musicvideodetails} if($type eq 'musicvideo'); - if($base) { - readingsBeginUpdate($hash); - readingsBulkUpdate($hash,'playStatus','playing') if($event->{event} eq 'Player.OnPlay'); - readingsBulkUpdate($hash,'type',$type); - foreach my $key (keys %$base) { - my $item = $base->{$key}; - XBMC_CreateReading($hash,$key,$item); - } - readingsEndUpdate($hash, 1); - } - $hash->{PendingEvents}{$id} = undef; + $base = $obj->{result}->{episodedetails} if($type eq 'episode'); + $base = $obj->{result}->{moviedetails} if($type eq 'movie'); + $base = $obj->{result}->{musicvideodetails} if($type eq 'musicvideo'); + if($base) { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,'playStatus','playing') if($event->{event} eq 'Player.OnPlay'); + readingsBulkUpdate($hash,'type',$type); + foreach my $key (keys %$base) { + my $item = $base->{$key}; + XBMC_CreateReading($hash,$key,$item); + } + readingsEndUpdate($hash, 1); + } + $hash->{PendingEvents}{$id} = undef; } elsif(defined($hash->{PendingPlayerCMDs}{$id})) { my $cmd = $hash->{PendingPlayerCMDs}{$id}; - my $players = $obj->{result}; - foreach my $player (@$players) { - $cmd->{id} = XBMC_CreateId(); - $cmd->{params}->{playerid} = $player->{playerid}; - XBMC_Call($hash,$cmd,1); - } - $hash->{PendingPlayerCMDs}{$id} = undef; + my $players = $obj->{result}; + foreach my $player (@$players) { + $cmd->{id} = XBMC_CreateId(); + $cmd->{params}->{playerid} = $player->{playerid}; + XBMC_Call($hash,$cmd,1); + } + $hash->{PendingPlayerCMDs}{$id} = undef; } else { my $properties = $obj->{result}; - if($properties && $properties ne 'OK') { - readingsBeginUpdate($hash); - foreach my $key (keys %$properties) { - my $value = $properties->{$key}; - XBMC_CreateReading($hash,$key,$value); - } - readingsEndUpdate($hash, 1); - } + if($properties && $properties ne 'OK') { + if ($properties ne 'pong') { + readingsBeginUpdate($hash); + foreach my $key (keys %$properties) { + my $value = $properties->{$key}; + XBMC_CreateReading($hash,$key,$value); + } + readingsEndUpdate($hash, 1); + } + else { + #Pong + $hash->{LAST_PONG} = time(); + } + } } return undef; } @@ -378,15 +496,15 @@ sub XBMC_CreateReading($$$) { my $key = shift; my $value = shift; if($key eq 'version') { - my $version = ''; - $version = $value->{major}; - $version .= '.' . $value->{minor} if(defined($value->{minor})); - $version .= '-' . $value->{revision} if(defined($value->{revision})); - $version .= ' ' . $value->{tag} if(defined($value->{tag})); - $value = $version; + my $version = ''; + $version = $value->{major}; + $version .= '.' . $value->{minor} if(defined($value->{minor})); + $version .= '-' . $value->{revision} if(defined($value->{revision})); + $version .= ' ' . $value->{tag} if(defined($value->{tag})); + $value = $version; } elsif($key eq 'skin') { - $value = $value->{name} . '(' . $value->{id} . ')'; + $value = $value->{name} . '(' . $value->{id} . ')'; } elsif($key eq 'totaltime' || $key eq 'time') { $value = sprintf('%02d:%02d:%02d.%03d',$value->{hours},$value->{minutes},$value->{seconds},$value->{milliseconds}); @@ -490,6 +608,15 @@ sub XBMC_Set($@) elsif($cmd eq 'open') { return XBMC_Set_Open($hash, 'file', @args); } + elsif($cmd eq 'openmovieid') { + return XBMC_Set_Open($hash, 'movie', @args); + } + elsif($cmd eq 'openepisodeid') { + return XBMC_Set_Open($hash, 'episode', @args); + } + elsif($cmd eq 'addon') { + return XBMC_Set_Addon($hash, @args); + } elsif($cmd eq 'shuffle') { return XBMC_Set_Shuffle($hash, @args); } @@ -530,6 +657,10 @@ sub XBMC_Set($@) my $action = $args[0]; #http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#Input.Action return XBMC_Call($hash,{'method' => 'Input.ExecuteAction', 'params' => { 'action' => $action}},0); } + elsif($cmd eq 'jsonraw') { + my $action = join("",@args); + return XBMC_Call_raw($hash,$action,0); + } elsif($cmd eq 'showcodec') { return XBMC_Simple_Call($hash,'Input.ShowCodec'); } @@ -582,60 +713,60 @@ sub XBMC_Set($@) #RPC referring to the VideoLibary - http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#VideoLibrary elsif($cmd eq 'videolibrary') { my $opt = $args[0]; - if($opt eq 'clean') { + if($opt eq 'clean') { return XBMC_Simple_Call($hash,'VideoLibrary.Clean'); - } - elsif($opt eq 'scan') { - return XBMC_Simple_Call($hash,'VideoLibrary.Scan'); - } + } + elsif($opt eq 'scan') { + return XBMC_Simple_Call($hash,'VideoLibrary.Scan'); + } } #RPC referring to the AudioLibary - http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6#AudioLibrary elsif($cmd eq 'audiolibrary') { my $opt = $args[0]; - if($opt eq 'clean') { + if($opt eq 'clean') { return XBMC_Simple_Call($hash,'AudioLibrary.Clean'); - } - elsif($opt eq 'scan') { - return XBMC_Simple_Call($hash,'AudioLibrary.Scan'); - } + } + elsif($opt eq 'scan') { + return XBMC_Simple_Call($hash,'AudioLibrary.Scan'); + } } my $res = "Unknown argument " . $cmd . ", choose one of " . - "off play:all,audio,video,picture playpause:all,audio,video,picture pause:all,audio,video,picture " . - "prev:all,audio,video,picture next:all,audio,video,picture goto stop:all,audio,video,picture " . - "open opendir shuffle:toggle,on,off repeat:one,all,off volumeUp:noArg volumeDown:noArg " . - "back:noArg contextmenu:noArg down:noArg home:noArg info:noArg left:noArg " . - "right:noArg select:noArg send exec:left,right," . - "up,down,pageup,pagedown,select,highlight,parentdir,parentfolder,back," . - "previousmenu,info,pause,stop,skipnext,skipprevious,fullscreen,aspectratio," . - "stepforward,stepback,bigstepforward,bigstepback,osd,showsubtitles," . - "nextsubtitle,codecinfo,nextpicture,previouspicture,zoomout,zoomin," . - "playlist,queue,zoomnormal,zoomlevel1,zoomlevel2,zoomlevel3,zoomlevel4," . - "zoomlevel5,zoomlevel6,zoomlevel7,zoomlevel8,zoomlevel9,nextcalibration," . - "resetcalibration,analogmove,rotate,rotateccw,close,subtitledelayminus," . - "subtitledelay,subtitledelayplus,audiodelayminus,audiodelay,audiodelayplus," . - "subtitleshiftup,subtitleshiftdown,subtitlealign,audionextlanguage," . - "verticalshiftup,verticalshiftdown,nextresolution,audiotoggledigital," . - "number0,number1,number2,number3,number4,number5,number6,number7," . - "number8,number9,osdleft,osdright,osdup,osddown,osdselect,osdvalueplus," . - "osdvalueminus,smallstepback,fastforward,rewind,play,playpause,delete," . - "copy,move,mplayerosd,hidesubmenu,screenshot,rename,togglewatched,scanitem," . - "reloadkeymaps,volumeup,volumedown,mute,backspace,scrollup,scrolldown," . - "analogfastforward,analogrewind,moveitemup,moveitemdown,contextmenu,shift," . - "symbols,cursorleft,cursorright,showtime,analogseekforward,analogseekback," . - "showpreset,presetlist,nextpreset,previouspreset,lockpreset,randompreset," . - "increasevisrating,decreasevisrating,showvideomenu,enter,increaserating," . - "decreaserating,togglefullscreen,nextscene,previousscene,nextletter,prevletter," . - "jumpsms2,jumpsms3,jumpsms4,jumpsms5,jumpsms6,jumpsms7,jumpsms8,jumpsms9,filter," . - "filterclear,filtersms2,filtersms3,filtersms4,filtersms5,filtersms6,filtersms7," . - "filtersms8,filtersms9,firstpage,lastpage,guiprofile,red,green,yellow,blue," . - "increasepar,decreasepar,volampup,volampdown,channelup,channeldown," . - "previouschannelgroup,nextchannelgroup,leftclick,rightclick,middleclick," . - "doubleclick,wheelup,wheeldown,mousedrag,mousemove,noop showcodec:noArg showosd:noArg up:noArg " . - "msg " . - "mute:toggle,on,off volume:slider,0,1,100 quit:noArg " . - "eject:noArg hibernate:noArg reboot:noArg shutdown:noArg suspend:noArg " . - "videolibrary:scan,clean audiolibrary:scan,clean statusRequest"; + "off play:all,audio,video,picture playpause:all,audio,video,picture pause:all,audio,video,picture " . + "prev:all,audio,video,picture next:all,audio,video,picture goto stop:all,audio,video,picture " . + "open opendir openmovieid openepisodeid addon shuffle:toggle,on,off repeat:one,all,off volumeUp:noArg volumeDown:noArg " . + "back:noArg contextmenu:noArg down:noArg home:noArg info:noArg left:noArg " . + "right:noArg select:noArg send exec:left,right," . + "up,down,pageup,pagedown,select,highlight,parentdir,parentfolder,back," . + "previousmenu,info,pause,stop,skipnext,skipprevious,fullscreen,aspectratio," . + "stepforward,stepback,bigstepforward,bigstepback,osd,showsubtitles," . + "nextsubtitle,codecinfo,nextpicture,previouspicture,zoomout,zoomin," . + "playlist,queue,zoomnormal,zoomlevel1,zoomlevel2,zoomlevel3,zoomlevel4," . + "zoomlevel5,zoomlevel6,zoomlevel7,zoomlevel8,zoomlevel9,nextcalibration," . + "resetcalibration,analogmove,rotate,rotateccw,close,subtitledelayminus," . + "subtitledelay,subtitledelayplus,audiodelayminus,audiodelay,audiodelayplus," . + "subtitleshiftup,subtitleshiftdown,subtitlealign,audionextlanguage," . + "verticalshiftup,verticalshiftdown,nextresolution,audiotoggledigital," . + "number0,number1,number2,number3,number4,number5,number6,number7," . + "number8,number9,osdleft,osdright,osdup,osddown,osdselect,osdvalueplus," . + "osdvalueminus,smallstepback,fastforward,rewind,play,playpause,delete," . + "copy,move,mplayerosd,hidesubmenu,screenshot,rename,togglewatched,scanitem," . + "reloadkeymaps,volumeup,volumedown,mute,backspace,scrollup,scrolldown," . + "analogfastforward,analogrewind,moveitemup,moveitemdown,contextmenu,shift," . + "symbols,cursorleft,cursorright,showtime,analogseekforward,analogseekback," . + "showpreset,presetlist,nextpreset,previouspreset,lockpreset,randompreset," . + "increasevisrating,decreasevisrating,showvideomenu,enter,increaserating," . + "decreaserating,togglefullscreen,nextscene,previousscene,nextletter,prevletter," . + "jumpsms2,jumpsms3,jumpsms4,jumpsms5,jumpsms6,jumpsms7,jumpsms8,jumpsms9,filter," . + "filterclear,filtersms2,filtersms3,filtersms4,filtersms5,filtersms6,filtersms7," . + "filtersms8,filtersms9,firstpage,lastpage,guiprofile,red,green,yellow,blue," . + "increasepar,decreasepar,volampup,volampdown,channelup,channeldown," . + "previouschannelgroup,nextchannelgroup,leftclick,rightclick,middleclick," . + "doubleclick,wheelup,wheeldown,mousedrag,mousemove,noop showcodec:noArg showosd:noArg up:noArg " . + "msg " . + "mute:toggle,on,off volume:slider,0,1,100 quit:noArg " . + "eject:noArg hibernate:noArg reboot:noArg shutdown:noArg suspend:noArg " . + "videolibrary:scan,clean audiolibrary:scan,clean statusRequest jsonraw"; return $res ; } @@ -665,6 +796,24 @@ sub XBMC_Set_Open($@) 'directory' => $path } }; + } elsif($opt eq 'movie') { + $params = { + 'item' => { + 'movieid' => $path +0 + }, + 'options' => { + 'resume' => JSON::true + } + }; + } elsif($opt eq 'episode') { + $params = { + 'item' => { + 'episodeid' => $path +0 + }, + 'options' => { + 'resume' => JSON::true + } + }; } my $obj = { 'method' => 'Player.Open', @@ -673,6 +822,29 @@ sub XBMC_Set_Open($@) return XBMC_Call($hash,$obj,0); } +sub XBMC_Set_Addon($@) +{ + my $hash = shift; + my $params; + my $attr = join(" ", @_); + $attr =~ /(".*?"|'.*?'|[^ ]+)[ \t]+(".*?"|'.*?'|[^ ]+)[ \t]+(".*?"|'.*?'|[^ ]+)$/; + my $addonid = $1; + my $paramname = $2; + my $paramvalue = $3; +# printf "$1 $2 $3"; + $params = { + 'addonid' => $addonid, + 'params' => { + $paramname => $paramvalue + } + }; + my $obj = { + 'method' => 'Addons.ExecuteAddon', + 'params' => $params + }; + return XBMC_Call($hash,$obj,0); +} + sub XBMC_Set_Message($@) { my $hash = shift; @@ -737,15 +909,15 @@ sub XBMC_Set_Shuffle($@) my $player = ''; if(int(@args) >= 2) { $toggle = $args[0]; - $player = $args[1]; + $player = $args[1]; } elsif(int(@args) == 1) { if($args[0] =~ /(all|audio|video|picture)/) { $player = $args[0]; - } - else { - $toggle = $args[0]; - } + } + else { + $toggle = $args[0]; + } } my $type = XBMC_Toggle($toggle); @@ -785,15 +957,15 @@ sub XBMC_Set_PlayPause($@) my $player = ''; if(int(@args) >= 2) { $toggle = $args[0]; - $player = $args[1]; + $player = $args[1]; } elsif(int(@args) == 1) { if($args[0] =~ /(all|audio|video|picture)/) { $player = $args[0]; - } - else { - $toggle = $args[0]; - } + } + else { + $toggle = $args[0]; + } } my $type = XBMC_Toggle($toggle); @@ -815,16 +987,16 @@ sub XBMC_PlayerCommand($$$) $id = 0 if($player eq "audio"); $id = 1 if($player eq "video"); $id = 2 if($player eq "picture"); - if($id > 0 && $id < 3) { - $obj->{params}->{playerid} = $id; - return XBMC_Call($hash, $obj,0); - } + if($id > 0 && $id < 3) { + $obj->{params}->{playerid} = $id; + return XBMC_Call($hash, $obj,0); + } } my $id = XBMC_CreateId(); $hash->{PendingPlayerCMDs}->{$id} = $obj; my $req = { 'method' => 'Player.GetActivePlayers', - 'id' => $id + 'id' => $id }; return XBMC_Call($hash,$req,1); } @@ -860,12 +1032,14 @@ sub XBMC_Set_Mute($@) sub XBMC_Call($$$) { my ($hash,$obj,$id) = @_; + my $name = $hash->{NAME}; #add an ID otherwise XBMC will not respond if($id &&!defined($obj->{id})) { $obj->{id} = XBMC_CreateId(); } $obj->{jsonrpc} = "2.0"; #JSON RPC version has to be passed my $json = encode_json($obj); + Log3($name, 5, "XBMC_Call: Sending: " . $json); if($hash->{Protocol} eq 'http') { return XBMC_HTTP_Call($hash,$json,$id); } @@ -874,6 +1048,19 @@ sub XBMC_Call($$$) } } +sub XBMC_Call_raw($$$) +{ + my ($hash,$obj,$id) = @_; + my $name = $hash->{NAME}; + Log3($name, 5, "XBMC_Call: Sending: " . $obj); + if($hash->{Protocol} eq 'http') { + return XBMC_HTTP_Call($hash,$obj,$id); + } + else { + return XBMC_TCP_Call($hash,$obj); + } +} + sub XBMC_CreateId() { return int(rand(1000000)); @@ -918,8 +1105,8 @@ sub XBMC_HTTP_Call($$$) if($ret =~ /^error:(\d{3})$/) { return "HTTP Error Code " . $1; } - return XBMC_ProcessResponse($hash,decode_json($ret)) if($id); - return undef; + return XBMC_ProcessResponse($hash,JSON->new->utf8(0)->decode($ret)) if($id); + return undef; } #adapted version of the CustomGetFileFromURL subroutine from HttpUtils.pm @@ -930,7 +1117,7 @@ sub XBMC_HTTP_Request($$@) my $displayurl= $quiet ? "" : $url; if($url !~ /^(http|https):\/\/([^:\/]+)(:\d+)?(\/.*)$/) { - Log 1, "XBMC_HTTP_Request $displayurl: malformed or unsupported URL"; + Log(1, "XBMC_HTTP_Request $displayurl: malformed or unsupported URL"); return undef; } @@ -948,7 +1135,7 @@ sub XBMC_HTTP_Request($$@) if($protocol eq "https") { eval "use IO::Socket::SSL"; if($@) { - Log 1, $@; + Log(1, $@); } else { $conn = IO::Socket::SSL->new(PeerAddr=>"$host:$port", Timeout=>$timeout); } @@ -956,7 +1143,7 @@ sub XBMC_HTTP_Request($$@) $conn = IO::Socket::INET->new(PeerAddr=>"$host:$port", Timeout=>$timeout); } if(!$conn) { - Log 1, "XBMC_HTTP_Request $displayurl: Can't connect to $protocol://$host:$port\n"; + Log(1, "XBMC_HTTP_Request $displayurl: Can't connect to $protocol://$host:$port\n"); undef $conn; return undef; } @@ -965,12 +1152,12 @@ sub XBMC_HTTP_Request($$@) my $hdr = ($data ? "POST" : "GET")." $path HTTP/1.0\r\nHost: $host\r\n"; if($username) { $hdr .= "Authorization: Basic "; - if($password) { - $hdr .= encode_base64($username . ":" . $password,"\r\n"); - } - else { - $hdr .= encode_base64($username,"\r\n"); - } + if($password) { + $hdr .= encode_base64($username . ":" . $password,"\r\n"); + } + else { + $hdr .= encode_base64($username,"\r\n"); + } } if(defined($data)) { $hdr .= "Content-Length: ".length($data)."\r\n"; @@ -988,7 +1175,7 @@ sub XBMC_HTTP_Request($$@) vec($rin, $conn->fileno(), 1) = 1; my $nfound = select($rout=$rin, undef, undef, $timeout); if($nfound <= 0) { - Log 1, "XBMC_HTTP_Request $displayurl: Select timeout/error: $!"; + Log(1, "XBMC_HTTP_Request $displayurl: Select timeout/error: $!"); undef $conn; return undef; } @@ -1001,11 +1188,11 @@ sub XBMC_HTTP_Request($$@) $ret=~ s/(.*?)\r\n\r\n//s; # Not greedy: switch off the header. my @header= split("\r\n", $1); my $hostpath= $quiet ? "" : $host . $path; - Log 4, "XBMC_HTTP_Request $displayurl: Got data, length: ".length($ret); + Log(4, "XBMC_HTTP_Request $displayurl: Got data, length: ".length($ret)); if(!length($ret)) { - Log 4, "XBMC_HTTP_Request $displayurl: Zero length data, header follows..."; + Log(4, "XBMC_HTTP_Request $displayurl: Zero length data, header follows..."); for (@header) { - Log 4, "XBMC_HTTP_Request $displayurl: $_"; + Log(4, "XBMC_HTTP_Request $displayurl: $_"); } } undef $conn; @@ -1030,26 +1217,26 @@ sub XBMC_HTTP_Request($$@)

This module allows you to control XBMC and receive events from XBMC. It can also be used to control Plex (see attribute compatibilityMode).

- - Prerequisites - + + Prerequisites + To receive events it is necessary to use TCP. The default TCP port is 9090. Username and password are optional for TCP. Be sure to enable JSON-RPC - for TCP. See here.

+ + If you just want to control XBMC you can use the HTTP instead of tcp. The username and password are required for HTTP. Be sure to enable JSON-RPC for HTTP. See here.

Example:



- - Remote control:
- There is an simple remote control layout for XBMC which contains the most basic buttons. To add the remote control to the webinterface execute the - following commands:

-
    - +


+ + Remote control:
+ There is an simple remote control layout for XBMC which contains the most basic buttons. To add the remote control to the webinterface execute the + following commands:

+
    + define <rc_name> remotecontrol #adds the remote control

    set <rc_name> layout XBMC_RClayout #sets the layout for the remote control

    set <rc_name> makenotify <XBMC_device> #links the buttons to the actions -
    -


- - Known issues:
+
+

+ + Known issues:
XBMC sometimes creates events twices. For example the Player.OnPlay event is created twice if play a song. Unfortunately this is a issue of XBMC. The fix of this bug is included in future version of XBMC (> 12.2). @@ -1086,58 +1273,62 @@ sub XBMC_HTTP_Request($$@)

This module supports the following commands:
- Player related commands:
-
    -
  • play [<all|audio|video|picture>] - starts the playback (might only work if previously paused). The second argument defines which player should be started. By default the active players will be started
  • -
  • pause [<all|audio|video|picture>] - pauses the playback
  • -
  • playpause [<all|audio|video|picture>] - toggles between play and pause for the given player
  • -
  • stop [<all|audio|video|picture>] - stop the playback
  • -
  • next [<all|audio|video|picture>] - jump to the next track
  • -
  • prev [<all|audio|video|picture>] - jump to the previous track or the beginning of the current track.
  • -
  • goto <position> [<audio|video|picture>] - Goes to the in the playlist. has to be a number.
  • -
  • shuffle [<toggle|on|off>] [<audio|video|picture>] - Enables/Disables shuffle mode. Without furhter parameters the shuffle mode is toggled.
  • -
  • repeat <one|all|off> [<audio|video|picture>] - Sets the repeat mode.
  • -
  • open <URI> - Plays the resource located at the URI (can be a url or a file)
  • -
  • opendir <path> - Plays the content of the directory
  • -
-
Input related commands:
-
    -
  • back - Back-button
  • -
  • down - Down-button
  • -
  • up - Up-button
  • -
  • left - Left-button
  • -
  • right - Right-button
  • -
  • home - Home-button
  • -
  • select - Select-button
  • -
  • info - Info-button
  • -
  • showosd - Opens the OSD (On Screen Display)
  • -
  • showcodec - Shows Codec information
  • -
  • exec <action> - Execute an input action. All available actions are listed here
  • -
  • send <text> - Sends <text> as input to XBMC
  • -
-
Libary related commands:
-
    -
  • videolibrary clean - Removes non-existing files from the video libary
  • -
  • videolibrary scan - Scan for new video files
  • -
  • audiolibrary clean - Removes non-existing files from the audio libary
  • -
  • audiolibrary scan - Scan for new audio files
  • -
-
Application related commands:
-
    -
  • mute [<0|1>] - 1 for mute; 0 for unmute; by default the mute status will be toggled
  • -
  • volume <n> - sets the volume to <n>. <n> must be a number between 0 and 100
  • -
  • volumeDown <n> - volume down
  • -
  • volumeUp <n> - volume up
  • -
  • quit - closes XBMC
  • -
  • off - depending on the value of the attribute "offMode" XBMC will be closed (see quit) or the system will be shut down, put into hibernation or stand by. Default is quit.
  • -
-
System related commands:
-
    -
  • eject - will eject the optical drive
  • -
  • shutdown - the XBMC host will be shut down
  • -
  • suspend - the XBMC host will be put into stand by
  • -
  • hibernate - the XBMC host will be put into hibernation
  • -
  • reboot - the XBMC host will be rebooted
  • + Player related commands:
    +
      +
    • play [<all|audio|video|picture>] - starts the playback (might only work if previously paused). The second argument defines which player should be started. By default the active players will be started
    • +
    • pause [<all|audio|video|picture>] - pauses the playback
    • +
    • playpause [<all|audio|video|picture>] - toggles between play and pause for the given player
    • +
    • stop [<all|audio|video|picture>] - stop the playback
    • +
    • next [<all|audio|video|picture>] - jump to the next track
    • +
    • prev [<all|audio|video|picture>] - jump to the previous track or the beginning of the current track.
    • +
    • goto <position> [<audio|video|picture>] - Goes to the in the playlist. has to be a number.
    • +
    • shuffle [<toggle|on|off>] [<audio|video|picture>] - Enables/Disables shuffle mode. Without furhter parameters the shuffle mode is toggled.
    • +
    • repeat <one|all|off> [<audio|video|picture>] - Sets the repeat mode.
    • +
    • open <URI> - Plays the resource located at the URI (can be a url or a file)
    • +
    • opendir <path> - Plays the content of the directory
    • +
    • openmovieid <path> - Plays the movie of the id
    • +
    • openepisodeid <path> - Plays the episode of the id
    • +
    • addon <addonid> <parametername> <parametervalue> - Executes addon with one Parameter, for example set xbmc addon script.json-cec command activate
    • +
    +
    Input related commands:
    +
      +
    • back - Back-button
    • +
    • down - Down-button
    • +
    • up - Up-button
    • +
    • left - Left-button
    • +
    • right - Right-button
    • +
    • home - Home-button
    • +
    • select - Select-button
    • +
    • info - Info-button
    • +
    • showosd - Opens the OSD (On Screen Display)
    • +
    • showcodec - Shows Codec information
    • +
    • exec <action> - Execute an input action. All available actions are listed here
    • +
    • send <text> - Sends <text> as input to XBMC
    • +
    • jsonraw - Sends raw JSON data to XBMC
    • +
    +
    Libary related commands:
    +
      +
    • videolibrary clean - Removes non-existing files from the video libary
    • +
    • videolibrary scan - Scan for new video files
    • +
    • audiolibrary clean - Removes non-existing files from the audio libary
    • +
    • audiolibrary scan - Scan for new audio files
    • +
    +
    Application related commands:
    +
      +
    • mute [<0|1>] - 1 for mute; 0 for unmute; by default the mute status will be toggled
    • +
    • volume <n> - sets the volume to <n>. <n> must be a number between 0 and 100
    • +
    • volumeDown <n> - volume down
    • +
    • volumeUp <n> - volume up
    • +
    • quit - closes XBMC
    • +
    • off - depending on the value of the attribute "offMode" XBMC will be closed (see quit) or the system will be shut down, put into hibernation or stand by. Default is quit.
    • +
    +
    System related commands:
    +
      +
    • eject - will eject the optical drive
    • +
    • shutdown - the XBMC host will be shut down
    • +
    • suspend - the XBMC host will be put into stand by
    • +
    • hibernate - the XBMC host will be put into hibernation
    • +
    • reboot - the XBMC host will be rebooted


@@ -1154,51 +1345,51 @@ sub XBMC_HTTP_Request($$@)
Generated Readings/Events:
    -
  • audiolibrary - Possible values: cleanfinished, cleanstarted, remove, scanfinished, scanstarted, update
  • -
  • currentAlbum - album of the current song/musicvideo
  • -
  • currentArtist - artist of the current song/musicvideo
  • -
  • currentMedia - file/URL of the media item being played
  • -
  • currentTitle - title of the current media item
  • -
  • currentTrack - track of the current song/musicvideo
  • -
  • episode - episode number
  • -
  • episodeid - id of the episode in the video library
  • -
  • fullscreen - indicates if XBMC runs in fullscreen mode (on/off)
  • -
  • label - label of the current media item
  • -
  • movieid - id of the movie in the video library
  • -
  • musicvideoid - id of the musicvideo in the video library
  • -
  • mute - indicates if XBMC is muted (on/off)
  • -
  • name - software name (e.g. XBMC)
  • -
  • originaltitle - original title of the movie being played
  • -
  • partymode - indicates if XBMC runs in party mode (on/off) (not available for Plex)
  • -
  • playlist - Possible values: add, clear, remove
  • -
  • playStatus - Indicates the player status: playing, paused, stopped
  • -
  • repeat - current repeat mode (one/all/off)
  • -
  • season - season of the current episode
  • -
  • showtitle - title of the show being played
  • -
  • shuffle - indicates if the playback is shuffled (on/off)
  • -
  • skin - current skin of XBMC
  • -
  • songid - id of the song in the music library
  • -
  • system - Possible values: lowbattery, quit, restart, sleep, wake
  • -
  • time - current position in the playing media item (only updated on play/pause)
  • -
  • totaltime - total run time of the current media item
  • -
  • type - type of the media item. Possible values: episode, movie, song, musicvideo, picture, unknown
  • -
  • version - version of XBMC
  • -
  • videolibrary - Possible values: cleanfinished, cleanstarted, remove, scanfinished, scanstarted, update
  • -
  • volume - value between 0 and 100 stating the current volume setting
  • -
  • year - year of the movie being played
  • +
  • audiolibrary - Possible values: cleanfinished, cleanstarted, remove, scanfinished, scanstarted, update
  • +
  • currentAlbum - album of the current song/musicvideo
  • +
  • currentArtist - artist of the current song/musicvideo
  • +
  • currentMedia - file/URL of the media item being played
  • +
  • currentTitle - title of the current media item
  • +
  • currentTrack - track of the current song/musicvideo
  • +
  • episode - episode number
  • +
  • episodeid - id of the episode in the video library
  • +
  • fullscreen - indicates if XBMC runs in fullscreen mode (on/off)
  • +
  • label - label of the current media item
  • +
  • movieid - id of the movie in the video library
  • +
  • musicvideoid - id of the musicvideo in the video library
  • +
  • mute - indicates if XBMC is muted (on/off)
  • +
  • name - software name (e.g. XBMC)
  • +
  • originaltitle - original title of the movie being played
  • +
  • partymode - indicates if XBMC runs in party mode (on/off) (not available for Plex)
  • +
  • playlist - Possible values: add, clear, remove
  • +
  • playStatus - Indicates the player status: playing, paused, stopped
  • +
  • repeat - current repeat mode (one/all/off)
  • +
  • season - season of the current episode
  • +
  • showtitle - title of the show being played
  • +
  • shuffle - indicates if the playback is shuffled (on/off)
  • +
  • skin - current skin of XBMC
  • +
  • songid - id of the song in the music library
  • +
  • system - Possible values: lowbattery, quit, restart, sleep, wake
  • +
  • time - current position in the playing media item (only updated on play/pause)
  • +
  • totaltime - total run time of the current media item
  • +
  • type - type of the media item. Possible values: episode, movie, song, musicvideo, picture, unknown
  • +
  • version - version of XBMC
  • +
  • videolibrary - Possible values: cleanfinished, cleanstarted, remove, scanfinished, scanstarted, update
  • +
  • volume - value between 0 and 100 stating the current volume setting
  • +
  • year - year of the movie being played


Remarks on the events

    The event playStatus = playing indicates a playback of a media item. Depending on the event type different events are generated: -
      -
    • type = song generated events are: album, artist, file, title and track
    • -
    • type = musicvideo generated events are: album, artist, file and title
    • -
    • type = episode generated events are: episode, file, season, showtitle, and title
    • -
    • type = movie generated events are: originaltitle, file, title, and year
    • -
    • type = picture generated events are: file
    • -
    • type = unknown generated events are: file
    • -
    +
      +
    • type = song generated events are: album, artist, file, title and track
    • +
    • type = musicvideo generated events are: album, artist, file and title
    • +
    • type = episode generated events are: episode, file, season, showtitle, and title
    • +
    • type = movie generated events are: originaltitle, file, title, and year
    • +
    • type = picture generated events are: file
    • +
    • type = unknown generated events are: file
    • +


@@ -1206,14 +1397,14 @@ sub XBMC_HTTP_Request($$@)
  • compatibilityMode
    This module can also be used to control Plex, since the JSON Api is mostly the same, but there are some differences. - If you want to control Plex set the attribute compatibilityMode to plex.
  • + If you want to control Plex set the attribute compatibilityMode to plex.
  • offMode
    Declares what should be down if the off command is executed. Possible values are quit (closes XBMC), hibernate (puts system into hibernation), - suspend (puts system into stand by), and shutdown (shuts down the system). Default value is quit
  • -
  • fork
    + suspend (puts system into stand by), and shutdown (shuts down the system). Default value is quit
  • +
  • fork
    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 attr <XBMC_device> fork enable - which will move the search for XBMC into a separate process.
  • + if TCP was used). If you encounter problems like FHEM not responding for a few seconds then you should set attr <XBMC_device> fork enable + which will move the search for XBMC into a separate process.