From b7b4c6e03aa125a60400c5cce26cfb7239a23c79 Mon Sep 17 00:00:00 2001 From: justme-1968 Date: Sat, 29 Dec 2018 11:51:07 +0000 Subject: [PATCH] 37_harmony.pm: try to get remoteId with fake origin query if broadcast fails git-svn-id: https://svn.fhem.de/fhem/trunk@18083 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/37_harmony.pm | 150 ++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 29 deletions(-) diff --git a/fhem/FHEM/37_harmony.pm b/fhem/FHEM/37_harmony.pm index abcd2d275..1baa5ba88 100644 --- a/fhem/FHEM/37_harmony.pm +++ b/fhem/FHEM/37_harmony.pm @@ -80,6 +80,7 @@ harmony_startDiscovery() FD => $socket->fileno(), PORT => $socket->sockport, }; + $chash->{helper}{discovered} = {}; $attr{$chash->{NAME}}{room} = 'hidden'; @@ -101,12 +102,16 @@ harmony_startDiscovery() sub harmony_sendDiscovery() { - my $chash = $modules{harmony}{defptr}{'harmony:discovery'}; - return if( !$chash ); - Log3 undef, 3, "harmony: sending discovery" ; + if( my $chash = $modules{harmony}{defptr}{'harmony:discovery'} ) { + Log3 undef, 3, "harmony: sending discovery" ; - my $sin = sockaddr_in(5224, inet_aton('255.255.255.255')); - $chash->{sendSocket}->send( "_logitech-reverse-bonjour._tcp.local.\n$chash->{PORT}", 0, $sin ); + my $sin = sockaddr_in(5224, inet_aton('255.255.255.255')); + $chash->{sendSocket}->send( "_logitech-reverse-bonjour._tcp.local.\n$chash->{PORT}", 0, $sin ); + + } else { + Log3 undef, 2, "harmony: can't send or receive discovery" ; + + } foreach my $chash ( values %{$modules{'harmony'}{defptr}} ) { next if( $chash->{NAME} eq 'harmony:discovery' ); @@ -115,14 +120,60 @@ harmony_sendDiscovery() next if( $chash->{remoteId} ); RemoveInternalTimer($chash); - InternalTimer(gettimeofday()+10, "harmony_tryXMPP", $chash, 0); + InternalTimer(gettimeofday()+10, "harmony_tryFakeOrigin", $chash, 0); } } +sub +harmony_tryFakeOrigin($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 3, "$name no discovery response received, trying fake origin" ; + my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 2; + if( my $socket = IO::Socket::INET->new(PeerAddr=>"$hash->{ip}:8088", Timeout=>$timeout) ) { + Log3 $name, 3, "$name: connected"; + $hash->{protocol} = "WEBSOCKET"; + $hash->{ConnectionState} = "Connected"; + readingsSingleUpdate( $hash, "state", $hash->{ConnectionState}, 1 ) if( $hash->{ConnectionState} ne ReadingsVal($name, "state", "" ) ); + $hash->{LAST_CONNECT} = FmtDateTime( gettimeofday() ); + + $hash->{FD} = $socket->fileno(); + $hash->{CD} = $socket; # sysread / close won't work on fileno + $hash->{CONNECTS}++; + $selectlist{$name} = $hash; + + $hash->{fakeOrigin} = 1; + + my $ret = "POST / HTTP/1.1\r\n"; + $ret .= harmony_hash2header( { 'Host' => "$hash->{ip}:8088", + 'Origin' => 'http://localhost.nebula.myharmony.com', + 'Content-Type' => 'application/json', + 'Content-Length' => 35, + } ); + + $ret .= "\r\n"; + $ret .= '{"cmd":"connect.discoveryinfo?get"}'; + Log3 $name, 5, "$name: $ret"; + + syswrite( $hash->{CD}, $ret ); + + return; + + } else { + Log3 $name, 2, "$name failed to connect to websocket port" ; + + } + + harmony_tryXMPP($hash); +} + sub harmony_tryXMPP($) { my ($hash) = @_; my $name = $hash->{NAME}; + Log3 $name, 3, "$name no discovery response received, trying fallback to xmpp" ; harmony_connect($hash); @@ -1186,13 +1237,15 @@ harmony_Read($) } Log3 $name, 4, Dumper \%params; + $hash->{helper}{discovered}{$params{remoteId}} = \%params; + foreach my $chash ( values %{$modules{$hash->{TYPE}}{defptr}} ) { next if( $chash->{NAME} eq 'harmony:discovery' ); next if( !defined($chash->{ip}) ); next if( $chash->{remoteId} ); if( $chash->{ip} eq $params{ip} ) { - $chash->{remoteId} = $params{remoteId}; $chash->{discoveryinfo} = \%params; + $chash->{remoteId} = $chash->{discoveryinfo}{remoteId}; Log3 $name, 4, Dumper $chash->{discoveryinfo}{protocolVersion}; if( $chash->{discoveryinfo}{protocolVersion} =~ m/XMPP/ ) { @@ -1219,33 +1272,33 @@ harmony_Read($) $close = 1; } elsif( $hash->{websocket} ) { - $hash->{buf} .= $buf; + $hash->{helper}{PARTIAL} .= $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 $fin = (ord(substr($hash->{helper}{PARTIAL},0,1)) & 0x80)?1:0; + my $op = (ord(substr($hash->{helper}{PARTIAL},0,1)) & 0x0F); + my $mask = (ord(substr($hash->{helper}{PARTIAL},1,1)) & 0x80)?1:0; + my $len = (ord(substr($hash->{helper}{PARTIAL},1,1)) & 0x7F); my $i = 2; #Log 1, $len; if( $len == 126 ) { - $len = unpack( 'n', substr($hash->{buf},$i,2) ); + $len = unpack( 'n', substr($hash->{helper}{PARTIAL},$i,2) ); $i += 2; } elsif( $len == 127 ) { - $len = unpack( 'N', substr($hash->{buf},$i+4,6) ); + $len = unpack( 'N', substr($hash->{helper}{PARTIAL},$i+4,6) ); $i += 8; } if( $mask ) { - $mask = substr($hash->{buf},$i,4); + $mask = substr($hash->{helper}{PARTIAL},$i,4); $i += 4; } #Log 1, "$fin $op $mask $len"; - return if( $len > length($hash->{buf})-$i ); + return if( $len > length($hash->{helper}{PARTIAL})-$i ); - my $data = substr($hash->{buf}, $i, $len); - $hash->{buf} = substr($hash->{buf},$i+$len); + my $data = substr($hash->{helper}{PARTIAL}, $i, $len); + $hash->{helper}{PARTIAL} = substr($hash->{helper}{PARTIAL},$i+$len); if( $op == 0x01 ) { my $json = harmony_decode_json($data); @@ -1265,7 +1318,7 @@ harmony_Read($) } - } while( $hash->{buf} && !$close ); + } while( $hash->{helper}{PARTIAL} && !$close ); } elsif( $buf =~ m'^HTTP/1.1 101 Switching Protocols'i ) { $hash->{websocket} = 1; @@ -1305,9 +1358,41 @@ harmony_Read($) return; } + my $data = $hash->{helper}{PARTIAL}; $data .= $buf; + if( $hash->{fakeOrigin} ) { + if( $data =~ m/200 OK/ ) { + if( $data =~ m/^(.*?)\r?\n\r?\n(.*)$/s ) { + my $header = $1; + my $body = $2; + + if( my $json = harmony_decode_json($body) ) { + Log3 $name, 3, "$name: answer for fake origin query received"; + $hash->{discoveryinfo} = $json->{data}; + $hash->{remoteId} = $hash->{discoveryinfo}{remoteId}; + + Log3 $name, 4, Dumper $hash->{discoveryinfo}{protocolVersion}; + if( $hash->{discoveryinfo}{protocolVersion} =~ m/XMPP/ ) { + delete $hash->{remoteId} if( !AttrVal( $hash->{NAME}, 'forceWebSocket', 0 ) ); + } + + harmony_connect( $hash ); + } + + } else { + $hash->{helper}{PARTIAL} = $data; + } + + } else { + harmony_tryXMPP($hash); + + } + + return; + } + #FIXME: should use real xmpp/xml parser # see forum https://forum.fhem.de/index.php/topic,14163.msg575033.html#msg575033 $data =~ s///g; @@ -1629,6 +1714,10 @@ harmony_disconnect($) $hash->{ConnectionState} = "Disconnected"; readingsSingleUpdate( $hash, "state", $hash->{ConnectionState}, 1 ) if( $hash->{ConnectionState} ne ReadingsVal($name, "state", "" ) ); + delete $hash->{websocket}; + delete $hash->{fakeOrigin}; + $hash->{helper}{PARTIAL} = ""; + return if( !$hash->{CD} ); Log3 $name, 2, "$name: disconnect"; @@ -1666,10 +1755,6 @@ harmony_connect($) $hash->{CONNECTS}++; $selectlist{$name} = $hash; - $hash->{helper}{PARTIAL} = ""; - $hash->{buf} = ""; - delete $hash->{websocket}; - my $domain = "svcs.myharmony.com"; if( $hash->{discoveryinfo}{discoveryServerUri} =~ m'https://([^/]+)' ) { $domain = $1; @@ -1687,7 +1772,7 @@ harmony_connect($) $ret .= "\r\n"; Log3 $name, 5, "$name: $ret"; - syswrite($hash->{CD}, $ret ); + syswrite( $hash->{CD}, $ret ); } else { harmony_disconnect( $hash ); @@ -1715,8 +1800,6 @@ harmony_connect($) $hash->{CONNECTS}++; $selectlist{$name} = $hash; - $hash->{helper}{PARTIAL} = ""; - harmony_send($hash, ""); } else { @@ -1785,7 +1868,7 @@ harmony_send($$) } } - syswrite($hash->{CD}, $txt ); + syswrite( $hash->{CD}, $txt ); } return; } @@ -1869,7 +1952,7 @@ harmony_ping($) if( $hash->{remoteId} ) { my $txt = chr(0x89) . chr(0); - syswrite($hash->{CD}, $txt ); + syswrite( $hash->{CD}, $txt ); RemoveInternalTimer($hash); InternalTimer(gettimeofday()+50, "harmony_ping", $hash, 0); @@ -2039,7 +2122,16 @@ harmony_Get($$@) my $list = "discovered:noArg"; if( $cmd eq "discovered" ) { - return; + my $ret; + foreach my $remoteId ( sort keys %{$hash->{helper}{discovered}}) { + my $discoveryinfo = $hash->{helper}{discovered}{$remoteId}; + $ret .= "\n" if( $ret ); + $ret .= sprintf( "%-8s %-15s %-20s %-9s %s", $discoveryinfo->{remoteId}, $discoveryinfo->{ip}, $discoveryinfo->{friendlyName}, $discoveryinfo->{current_fw_version}, $discoveryinfo->{protocolVersion} ); + } + $ret = sprintf( "%-8s %-15s %-20s %-9s %s\n", "remoteId", "ip", "friendlyName", "fw_vers", "protocolVersion" ).$ret if( $ret ); + $ret .= "\n" if( $ret ); + + return $ret; } return "Unknown argument $cmd, choose one of $list";