diff --git a/fhem/FHEM/37_plex.pm b/fhem/FHEM/37_plex.pm index 31504911d..20ff1395e 100644 --- a/fhem/FHEM/37_plex.pm +++ b/fhem/FHEM/37_plex.pm @@ -13,6 +13,9 @@ use Sys::Hostname; use IO::Socket::INET; #use Net::Address::IP::Local; +#use MIME::Base64; + +use JSON; use Encode qw(encode); use XML::Simple qw(:strict); @@ -45,7 +48,7 @@ plex_Initialize($) $hash->{GetFn} = "plex_Get"; $hash->{AttrFn} = "plex_Attr"; $hash->{AttrList} = "disable:1,0" - . " httpPort ignoredClients ignoredServers" + . " fhemIP httpPort ignoredClients ignoredServers" . " removeUnusedReadings:1,0 responder:1,0" . " user password " . $readingFnAttributes; @@ -137,8 +140,8 @@ plex_Define($$) plex_sendApiCmd( $hash, "http://$hash->{server}:$hash->{port}/servers", "servers" ) if( $hash->{server} ); - } elsif( $hash->{STATE} ne "???" ) { - $hash->{STATE} = "Initialized"; + } else { + readingsSingleUpdate($hash, 'state', 'initialized', 1 ); } @@ -407,6 +410,7 @@ plex_startDiscovery($) #} } + readingsSingleUpdate($hash, 'state', 'running', 1 ); } sub plex_stopDiscovery($) @@ -755,7 +759,7 @@ plex_startTimelineListener($) plex_refreshSubscriptions($chash); } else { - Log3 $name, 3, "$name: failed to start timeline listener started: $@"; + Log3 $name, 3, "$name: failed to start timeline listener on port $port $@"; InternalTimer(gettimeofday()+10, "plex_startTimelineListener", $hash, 0); } @@ -796,6 +800,7 @@ plex_Undefine($$) my ($hash, $arg) = @_; plex_stopTimelineListener($hash); + plex_stopWebsockets($hash); plex_stopDiscovery($hash); delete $modules{plex}{defptr}{MASTER} if( $modules{plex}{defptr}{MASTER} == $hash ) ; @@ -1222,6 +1227,22 @@ plex_makeLink($$$$;$) return "$txt"; } +sub +plex_makeImage($$$$) +{ + my ($hash, $server, $url, $size) = @_; + + return '' if( !$url ); + + my $token = $server->{accessToken}; + $token = $hash->{token} if( !$token ); + + my $ret .= "{address}:$server->{port}/photo/:/transcode?X-Plex-Token=$token&url=". + urlEncode("127.0.0.1:32400$url?X-Plex-Token=$token") + ."&width=$size&height=$size\">\n"; + + return $ret; +} sub @@ -1248,7 +1269,7 @@ plex_mediaList2($$$$) $ret .= sprintf( "%-35s %-10s %s\n", 'key', 'type', 'title' ); foreach my $item (@{$items}) { $item->{type} = '' if( !$item->{type} ); - + $item->{title} = encode('UTF-8', $item->{title}); $ret .= plex_makeLink($hash, 'ls', $xml->{parentSection}, $item->{key}, sprintf( "%-35s %-10s %s", $item->{key}, $item->{type}, $item->{title} ) ); $ret .= " ($item->{year})" if( $item->{year} ); $ret .= "\n"; @@ -1262,8 +1283,9 @@ plex_mediaList2($$$$) $ret .= sprintf( "%-35s %-10s %s\n", 'key', 'type', 'title' ); foreach my $item (@{$items}) { $item->{type} = '' if( !$item->{type} ); + $item->{title} = encode('UTF-8', $item->{title}); $ret .= plex_makeLink($hash, 'ls', $xml->{parentSection}, $item->{key}, sprintf( "%-35s %-10s %s\n", $item->{key}, $item->{type}, $item->{title} ) ); - #$ret .= " {address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$item->{composite}") ."&width=100&height=100\">\n" if( $item->{composite} ); + #$ret .= plex_makeImage($hash, $server, $xml->{composite}, 100); } } @@ -1273,6 +1295,7 @@ plex_mediaList2($$$$) $ret .= "$type\n"; $ret .= sprintf( "%-35s %-10s nr %s\n", 'key', 'type', 'title' ); foreach my $item (@{$items}) { + $item->{title} = encode('UTF-8', $item->{title}); if( defined($item->{index}) ) { $ret .= plex_makeLink($hash, 'detail', $xml->{parentSection}, $item->{key}, sprintf( "%-35s %-10s %3i %s", $item->{key}, $item->{type}, $item->{index}, $item->{title} ) ); $ret .= plex_makeLink($hash,'detail', undef, $item->{grandparentKey}, " ($item->{grandparentTitle}" ) if( $item->{grandparentTitle} ); @@ -1292,6 +1315,7 @@ plex_mediaList2($$$$) $ret .= "$type\n"; $ret .= sprintf( "%-35s %-10s nr %s\n", 'key', 'type', 'title' ); foreach my $item (@{$items}) { + $item->{title} = encode('UTF-8', $item->{title}); $ret .= sprintf( "%-35s %-10s %3i %s\n", $item->{key}, $item->{type}, $item->{index}, $item->{title} ); } } @@ -1307,9 +1331,19 @@ plex_mediaList($$$) #Log 1, Dumper $xml; return $xml if( ref($xml) ne 'HASH' ); + my $token = $server->{accessToken}; + $token = $hash->{token} if( !$token ); + + $xml->{librarySectionTitle} = encode('UTF-8', $xml->{librarySectionTitle}) if( $xml->{librarySectionTitle} ); + $xml->{title} = encode('UTF-8', $xml->{title}) if( $xml->{title} ); + $xml->{title1} = encode('UTF-8', $xml->{title1}) if( $xml->{title1} ); + $xml->{title2} = encode('UTF-8', $xml->{title2}) if( $xml->{title2} ); + $xml->{title3} = encode('UTF-8', $xml->{title3}) if( $xml->{title3} ); + + my $ret = ''; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$xml->{thumb}") ."&width=100&height=100\">\n" if( $xml->{thumb} ); - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$xml->{composite}") ."&width=100&height=100\">\n" if( $xml->{composite} ); + $ret .= plex_makeImage($hash, $server, $xml->{thumb}, 100); + $ret .= plex_makeImage($hash, $server, $xml->{composite}, 100); $ret .= "$xml->{librarySectionTitle}: " if( $xml->{librarySectionTitle} ); $ret .= plex_makeLink($hash, 'detail', undef, $xml->{ratingKey}, "$xml->{title} ") if( $xml->{title} ); $ret .= plex_makeLink($hash, 'detail', undef, $xml->{grandparentRatingKey}, "$xml->{title1} ") if( $xml->{title1} ); @@ -1335,6 +1369,10 @@ sub plex_mediaDetail2($$$$) { my ($hash, $server, $xml, $items) = @_; + + my $token = $server->{accessToken}; + $token = $hash->{token} if( !$token ); + #Log 1, Dumper $xml; if( $items ) { @@ -1345,13 +1383,20 @@ plex_mediaDetail2($$$$) } } + $xml->{viewGroup} = encode('UTF-8', $xml->{viewGroup}) if( $xml->{viewGroup} ); + my $ret = ''; foreach my $item (@{$items}) { + $item->{grandparentTitle} = encode('UTF-8', $item->{grandparentTitle}) if( $item->{grandparentTitle} ); + $item->{parentTitle} = encode('UTF-8', $item->{parentTitle}) if( $item->{parentTitle} ); + $item->{title} = encode('UTF-8', $item->{title}) if( $item->{title} ); + $item->{summary} = encode('UTF-8', $item->{summary}) if( $item->{summary} ); + $ret .= "\n" if( $ret && (!$xml->{viewGroup} || ($xml->{viewGroup} ne 'track' && $xml->{viewGroup} ne 'secondary') ) ); if( $item->{type} eq 'playlist' ) { $ret .= sprintf( "%s ", $item->{title} ) if( $item->{title} ); $ret .= "\n"; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$item->{composite}") ."&width=250&height=250\">" if( $item->{composite} ); + $ret .= plex_makeImage($hash, $server, $item->{composite}, 250); $ret .= "\n"; $ret .= sprintf( "%s ", $item->{playlistType} ) if( $item->{playlistType} ); $ret .= sprintf( "%s ", plex_timestamp2date($item->{addedAt}) ) if( $item->{addedAt} ); @@ -1366,7 +1411,7 @@ plex_mediaDetail2($$$$) $ret .= sprintf("(S%02iE%02i)",$item->{parentIndex}, $item->{index} ) if( $item->{parentIndex} && $item->{type} ne 'season' ); #$ret .= sprintf("(S%02i)", $item->{index} ) if( $item->{index} && $item->{type} eq 'season' ); $ret .= "\n"; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$item->{thumb}") ."&width=250&height=250\">" if( $item->{thumb} ); + $ret .= plex_makeImage($hash, $server, $item->{thumb}, 250); $ret .= "\n"; if( $item->{Genre} ) { foreach my $genre ( @{$item->{Genre}}) { @@ -1392,7 +1437,7 @@ plex_mediaDetail2($$$$) $ret .= sprintf( "%s ", $item->{title} ) if( $item->{title} ); #$ret .= "\n"; $ret .= "\n"; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$item->{thumb}") ."&width=250&height=250\">" if( !$xml->{thumb} && $item->{thumb} ); + $ret .= plex_makeImage($hash, $server, $item->{thumb}, 250); #$ret .= "\n"; $ret .= sprintf( "%s ", $item->{contentRating} ) if( $item->{contentRating} ); $ret .= sprintf( "%i ", $item->{year} ) if( $item->{year} ); @@ -1409,7 +1454,7 @@ plex_mediaDetail2($$$$) $ret .= " "; $ret .= plex_sec2hms($item->{duration}/1000); $ret .= "\n"; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$item->{thumb}") ."&width=250&height=250\">" if( $item->{thumb} ); + $ret .= plex_makeImage($hash, $server, $item->{thumb}, 250); $ret .= "\n"; $ret .= sprintf( "%s ", $item->{contentRating} ) if( $item->{contentRating} ); $ret .= sprintf( "%s ", $item->{rating} ) if( $item->{rating} ); @@ -1456,9 +1501,17 @@ plex_mediaDetail($$$) return $xml if( ref($xml) ne 'HASH' ); + my $token = $server->{accessToken}; + $token = $hash->{token} if( !$token ); + + $xml->{title} = encode('UTF-8', $xml->{title}) if( $xml->{title} ); + $xml->{title1} = encode('UTF-8', $xml->{title1}) if( $xml->{title1} ); + $xml->{title2} = encode('UTF-8', $xml->{title2}) if( $xml->{title2} ); + $xml->{summary} = encode('UTF-8', $xml->{summary}) if( $xml->{summary} ); + #Log 1, Dumper $xml; my $ret = ''; - $ret .= "{address}:$server->{port}/photo/:/transcode?url=". urlEncode("127.0.0.1:32400$xml->{thumb}") ."&width=250&height=250\">\n" if( $xml->{thumb} ); + $ret .= plex_makeImage($hash, $server, $xml->{thumb}, 250); $ret .= plex_makeLink($hash, 'detail', undef, $xml->{ratingKey}, "$xml->{title} ") if( $xml->{title} ); $ret .= sprintf( "%s: ", $xml->{title1} ) if( $xml->{title1} ); $ret .= sprintf( "%s: ", $xml->{title2} ) if( $xml->{title2} ); @@ -1564,6 +1617,11 @@ plex_Get($$@) return undef if( !$xml ); return Dumper $xml; + } elsif( $cmd eq 'identity' ) { + my $xml = plex_sendApiCmd( $hash, "http://$ip:$entry->{port}/identity", 'identity', 1 ); + return undef if( !$xml ); + return Dumper $xml; + } elsif( $cmd eq 'detail' ) { return "usage: detail " if( !$param ); my $ret = plex_sendApiCmd( $hash, "http://$ip:$entry->{port}$param", 'detail', $hash->{CL} || 1 ); @@ -1598,7 +1656,7 @@ plex_Get($$@) } - $list .= 'ls search sessions:noArg detail onDeck:noArg recentlyAdded:noArg playlists:noArg '; + $list .= 'identity:noArg ls search sessions:noArg detail onDeck:noArg recentlyAdded:noArg playlists:noArg '; $list .= 'servers:noArg pin:noArg ' if( $list !~ m/\bservers\b/ ); } @@ -1694,11 +1752,14 @@ plex_Attr($$$) if( $attrName eq 'disable' ) { if( $cmd eq "set" && $attrVal ) { plex_stopTimelineListener($hash); + plex_stopWebsockets($hash); plex_stopDiscovery($hash); foreach my $ip ( keys %{$hash->{clients}} ) { $hash->{clients}{$ip}{online} = 0; } + readingsSingleUpdate($hash, 'state', 'disabled', 1 ); } else { + readingsSingleUpdate($hash, 'state', 'running', 1 ); $attr{$name}{$attrName} = 0; plex_startDiscovery($hash); plex_startTimelineListener($hash); @@ -1737,7 +1798,13 @@ plex_Attr($$$) delete $hash->{token}; plex_getToken($hash); } + } + } elsif( $attrName eq 'fhemIP' ) { + if( $cmd eq "set" && $attrVal ) { + $hash->{fhemIP} = $attrVal; + } else { + $hash->{fhemIP} = plex_getLocalIP(); } } @@ -2115,6 +2182,7 @@ plex_entryOfIP($$$) $hash->{$type.'s'} = {} if( !$hash->{$type.'s'} ); my $entries = $hash->{$type.'s'}; + foreach my $key ( keys %{$entries} ) { return $entries->{$key} if( $entries->{$key}{address} eq $ip ); } @@ -2144,9 +2212,9 @@ plex_serverOf($$;$) my $entry; $entry = plex_entryOfID($hash, 'server', $hash->{currentServer} ) if( $hash->{currentServer} ); - $entry = plex_entryOfIP($hash, 'server', $server) if( $server =~ m/^\d+\.\d+\.\d+\.\d+$/ ); + $entry = plex_entryOfIP($hash, 'server', $server) if( $server && $server =~ m/^\d+\.\d+\.\d+\.\d+$/ ); - $entry = plex_entryOfID($hash, 'server', $server) if( !$entry ); + $entry = plex_entryOfID($hash, 'server', $server) if( $server && !$entry ); $entry = plex_entryOfIP($hash, 'server', $hash->{server} ) if( !$entry ); @@ -2319,6 +2387,8 @@ plex_discovered($$$$) plex_sendApiCmd( $hash, "http://$ip:$entry->{port}/clients", "clients" ); } + plex_requestNotifications( $hash, $entry ); + } elsif( $type eq 'client' ) { Log3 $name, 3, "$name: $type discovered: $ip" if( $new ); @@ -2390,6 +2460,76 @@ plex_disappeared($$$) } } +sub +plex_requestNotifications($$) +{ + my ($hash,$server) = @_; + my $name = $hash->{NAME}; + + return if( $hash->{helper}{websockets}{$server->{machineIdentifier}} ); + + if( my $socket = IO::Socket::INET->new(PeerAddr=>"$server->{address}:$server->{port}", Timeout=>2, Blocking=>1, ReuseAddr=>1, ReusePort=>defined(&ReusePort)?1:0) ) { + + my $chash = plex_newChash( $hash, $socket, + {NAME=>"$name:websocket:$server->{machineIdentifier}", STATE=>'listening', websocket=>0} ); + + $chash->{address} = $server->{address}; + $chash->{machineIdentifier} = $server->{machineIdentifier}; + + Log3 $name, 3, "$name: notification websocket opened to $server->{address}"; + + $hash->{helper}{websockets}{$server->{machineIdentifier}} = $chash; + + + my $ret = "GET /:/websockets/notifications HTTP/1.1\r\n"; + $ret .= plex_hash2header( { 'Host' => "$server->{address}:$server->{port}", + 'X-Plex-Token' => $hash->{token}, + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Sec-WebSocket-Key' => 'RkhFTQ==', + 'Sec-WebSocket-Version' => '13', + } ); + + $ret .= "\r\n"; +#Log 1, $ret; + + syswrite($chash->{CD}, $ret ); + + } else { + Log3 $name, 2, "$name: failed to open notification websocket to $server->{address}"; + + } +} +sub +plex_closeNotifications($) +{ + my ($hash,$server) = @_; + my $name = $hash->{NAME}; +} +sub +plex_stopWebsockets($) +{ + my ($hash,$server) = @_; + my $name = $hash->{NAME}; + + return if( !$hash->{helper}{websockets} ); + + foreach my $key ( keys %{$hash->{helper}{websockets}} ) { + my $chash = $hash->{helper}{websockets}{$key}; + my $cname = $chash->{NAME}; + + plex_closeSocket($chash); + + delete($hash->{servers}{$chash->{address}}{sessions}); + delete($hash->{helper}{websockets}{$key}); + delete($defs{$cname}); + } + + Log3 $name, 3, "$name: websockets stoped"; +} + sub plex_readingsBulkUpdateIfChanged($$$) { @@ -2438,15 +2578,16 @@ plex_parseTimeline($$$) foreach my $entry (@{$xml->{Timeline}}) { next if( !$entry->{state} ); - if( $entry->{key} && $entry->{key} ne ReadingsVal($chash->{NAME}, 'key', '') ) { + my $key = $entry->{key}; + if( $key && $key ne ReadingsVal($chash->{NAME}, 'key', '') ) { $chash->{currentServer} = $entry->{machineIdentifier}; - readingsBulkUpdate($chash, 'key', $entry->{key} ); + readingsBulkUpdate($chash, 'key', $key ); readingsBulkUpdate($chash, 'server', $entry->{machineIdentifier} ); my $server = plex_entryOfID($hash, 'server', $entry->{machineIdentifier} ); $server = $entry if( !$server ); - plex_sendApiCmd( $hash, "http://$server->{address}:$server->{port}$entry->{key}", "#update:$chash->{NAME}" ); + plex_sendApiCmd( $hash, "http://$server->{address}:$server->{port}$key", "#update:$chash->{NAME}" ); } plex_readingsBulkUpdateIfChanged($chash, 'volume', $entry->{volume} ) if( $entry->{controllable} && $entry->{controllable} =~ m/\bvolume\b/ ); @@ -2457,13 +2598,14 @@ plex_parseTimeline($$$) $entries->{ $entry->{type} } = $entry; } - if( $entry->{time} ) { -# if( !$chash->{helper}{time} || abs($entry->{time} - $chash->{helper}{time}) > 2000 ) { -# plex_readingsBulkUpdateIfChanged($chash, 'time', plex_sec2hms($entry->{time}/1000) ); + my $time = $entry->{time}; + if( defined($time) ) { +# if( !$chash->{helper}{time} || abs($time - $chash->{helper}{time}) > 2000 ) { +# plex_readingsBulkUpdateIfChanged($chash, 'time', plex_sec2hms($time/1000) ); # -# $chash->{helper}{time} = $entry->{time}; +# $chash->{helper}{time} = $time; # } - $chash->{time} = $entry->{time}; + $chash->{time} = $time; } $chash->{seekRange} = $entry->{seekRange} if( $entry->{seekRange} && $entry->{seekRange} ne "0-0" ); @@ -3631,6 +3773,8 @@ plex_parseHttpAnswer($$$) my $chash = $defs{$1}; return undef if( !$chash ); +#Log 1, Dumper $xml; +#Log 1, Dumper $param; if( $xml->{librarySectionTitle} ne ReadingsVal($chash->{NAME}, 'section', '' ) ) { CommandDeleteReading( undef, "$chash->{NAME} currentAlbum|currentArtist|episode|series|track" ); } @@ -3720,6 +3864,20 @@ plex_parseHttpAnswer($$$) } + } elsif( $param->{key} eq 'sessions' ) { + $handled = 1; + + if( my $server = plex_serverOf($hash, $param->{host}) ) { + delete $server->{sessions}; + foreach my $type ( keys %{$xml} ) { + next if( ref($xml->{$type}) ne 'ARRAY' ); + + foreach my $item (@{$xml->{$type}}) { + $server->{sessions}{$item->{sessionKey}} = $item; + } + } + } + } elsif( $param->{key} =~ m/#m3u:(.*)/ ) { my $entry = plex_entryOfID($hash, 'server', $1); @@ -3869,6 +4027,156 @@ Log 1, "!!!!!!!!!!"; return undef; + } elsif( defined($hash->{websocket}) ) { + my $pname = $hash->{PNAME} || $name; + + $len = sysread($hash->{CD}, $buf, 10240); +#Log 1, "2:$len: $buf"; + my $peerhost = $hash->{CD}->peerhost; + my $peerport = $hash->{CD}->peerport; + + my $close = 0; + if( !defined($len) || !$len ) { + $close = 1; + + } elsif( $hash->{websocket} ) { + $hash->{buf} .= $buf; + + do { + my $fin = (ord(substr($hash->{buf},0,1)) & 0x80)?1:0; + my $op = (ord(substr($hash->{buf},0,1)) & 0x0F); + my $mask = (ord(substr($hash->{buf},1,1)) & 0x80)?1:0; + my $len = (ord(substr($hash->{buf},1,1)) & 0x7F); + my $i = 2; + if( $len == 126 ) { + $len = unpack( 'n', substr($hash->{buf},$i,2) ); + $i += 2; + } elsif( $len == 127 ) { + $len = unpack( 'q', substr($hash->{buf},$i,8) ); + $i += 8; + } + if( $mask ) { + $i += 4; + } +#Log 1, "$fin $op $mask $len"; + #FIXME: hande !$fin + return if( $len > length($hash->{buf})-$i ); + + my $data = substr($hash->{buf}, $i, $len); + $hash->{buf} = substr($hash->{buf},$i+$len); + + if( $op == 0x01 ) { + my $obj = eval { from_json($data) }; + + if( $obj ) { + my $phash = $hash->{phash}; + my $handled = 0; + + if( $obj->{_elementType} eq 'NotificationContainer' ) { + if( $obj->{type} eq 'playing' ) { + $handled = 1; + + my $cname; + my $session_info_requested; + + if( my $session = $obj->{_children}[0]{sessionKey} ) { + if( my $server = plex_serverOf($phash, $peerhost) ) { + if( my $session = $server->{sessions}{$session} ) { + if( my $chash = $modules{plex}{defptr}{$session->{Player}[0]{machineIdentifier}} ) { + $cname = $chash->{NAME}; +#Log 1, Dumper $obj; + readingsBeginUpdate($chash); + my $key = $obj->{_children}[0]{key}; + if( $key && $key ne ReadingsVal($chash->{NAME}, 'key', '') ) { + $chash->{currentServer} = $server->{machineIdentifier}; + + readingsBulkUpdate($chash, 'key', $key ); + readingsBulkUpdate($chash, 'server', $server->{machineIdentifier} ); + + plex_sendApiCmd( $phash, "http://$server->{address}:$server->{port}$key", "#update:$chash->{NAME}" ); + } + + my $time = $obj->{_children}[0]{viewOffset}; + if( defined($time) ) { +# if( !$chash->{helper}{time} || abs($time - $chash->{helper}{time}) > 2000 ) { +# plex_readingsBulkUpdateIfChanged($chash, 'time', plex_sec2hms($time/1000) ); +# +# $chash->{helper}{time} = $time; +# } + $chash->{time} = $time; + } + + plex_readingsBulkUpdateIfChanged($chash, 'state', $obj->{_children}[0]{state} ); + readingsEndUpdate($chash, 1); + + } else { + Log3 $pname, 3, "$pname: unknown player: $session->{Player}[0]{machineIdentifier}"; + } + } else { + Log3 $pname, 3, "$pname: new session $obj->{_children}[0]{sessionKey}"; + + $session_info_requested = 1; + plex_sendApiCmd( $phash, "http://$server->{address}:$server->{port}/status/sessions", 'sessions' ); + } + } + } else { + Log3 $pname, 3, "$pname: no session in notifcation "; + } + + if( !$session_info_requested ) { + if( $obj->{_children}[0]{state} eq 'playing' + || $obj->{_children}[0]{state} eq 'stopped' ) { + if( !$cname || $obj->{_children}[0]{key} ne ReadingsVal($cname, 'key', '' ) ) { + if( my $server = plex_serverOf($phash, $peerhost) ) { + plex_sendApiCmd( $phash, "http://$server->{address}:$server->{port}/status/sessions", 'sessions' ); + } + } + } + } + + } elsif( $obj->{type} eq 'status' ) { + $handled = 1; +#Log 1, Dumper $obj; + DoTrigger( $pname, "$obj->{_children}[0]{notificationName}: $obj->{_children}[0]{title}" ); + + } + } + + Log3 $pname, 4, "$pname: unhandled websocket text type: $obj->{type}: $data" if( !$handled ); + + } else { + Log3 $pname, 2, "$pname: unhandled websocket text $data"; + + } + + } else { + Log3 $pname, 2, "$pname: unhandled websocket data: $data"; + + } + + } while( $hash->{buf} && !$close ); + + } elsif( $buf =~ m'^HTTP/1.1 101 Switching Protocols'i ) { + $hash->{websocket} = 1; + my $buf = plex_msg2hash($buf, 1); + + Log3 $pname, 3, "$pname: notification websocket: Switching Protocols ok"; + + } else { + $close = 1; + Log3 $pname, 2, "$pname: notification websocket: Switching Protocols failed"; + } + + if( $close ) { + my $phash = $hash->{phash}; + plex_closeSocket( $hash ); + delete($phash->{helper}{websockets}{$hash->{machineIdentifier}}); + delete($phash->{servers}{$hash->{address}}{sessions}); + delete($defs{$name}); + } + + return undef; + } elsif ( $hash->{phash} ) { my $phash = $hash->{phash}; my $pname = $hash->{PNAME};