mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-07 23:09:26 +00:00
49_Arlo.pm: handle dropped connections, set connection inactive if no basestation is armed
git-svn-id: https://svn.fhem.de/fhem/trunk@20368 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
324c728e66
commit
344bab890f
@ -398,6 +398,11 @@ sub Arlo_PrepareRequest($$;$$$$) {
|
||||
$method = "GET" if (!defined($method));
|
||||
|
||||
my $account = $modules{$MODULE}{defptr}{"account"};
|
||||
|
||||
if ($account->{STATE} eq 'inactive') {
|
||||
Arlo_Login($account);
|
||||
}
|
||||
|
||||
my $name = $account->{NAME};
|
||||
my $cookies = $account->{helper}{cookies};
|
||||
my $token = $account->{helper}{token};
|
||||
@ -466,7 +471,8 @@ sub Arlo_DefaultCallback($$$) {
|
||||
$logLevel = 5;
|
||||
} elsif ($data->{error} eq '1022' && $data->{reason} eq 'Access token is invalid') {
|
||||
Log3 $name, 3, "Arlo access token was invalid. Reconnect to Arlo.";
|
||||
if ($hash->{STATE} eq 'active') {
|
||||
if ($account->{STATE} eq 'active') {
|
||||
$account->{RETRY} = 1;
|
||||
Arlo_Login($account);
|
||||
}
|
||||
$logLevel = 5;
|
||||
@ -607,7 +613,7 @@ sub Arlo_SubscribeCallback($$$) {
|
||||
my $response = Arlo_DefaultCallback($hash, $err, $jsonData);
|
||||
my $origin = $hash->{origin};
|
||||
if (defined($response) && $origin && ReadingsVal($origin->{NAME}, 'state', '') eq 'offline') {
|
||||
readingsSingleUpdate($hash, 'state', 'online', 1);
|
||||
readingsSingleUpdate($origin, 'state', 'online', 1);
|
||||
InternalTimer(gettimeofday() + 1, 'Arlo_UpdateBasestationReadings', $hash);
|
||||
}
|
||||
}
|
||||
@ -709,9 +715,37 @@ sub Arlo_SetModeReading($$) {
|
||||
}
|
||||
$modeName = $mode if (!defined($modeName) || $modeName eq '');
|
||||
readingsSingleUpdate($basestation, 'state', $modeName, 1);
|
||||
Arlo_CheckExpiry($account, $modeName);
|
||||
}
|
||||
}
|
||||
|
||||
sub Arlo_CheckExpiry($$) {
|
||||
my ($account, $modeName) = @_;
|
||||
my $expiryTime = AttrVal($account->{NAME}, 'expiryTime', 600);
|
||||
if ($account->{STATE} eq 'active' && $expiryTime > 0) {
|
||||
if ($modeName ne 'disarmed' && defined($account->{EXPIRY})) {
|
||||
delete $account->{EXPIRY};
|
||||
} elsif ($modeName eq 'disarmed' && Arlo_CheckBasestationsInactive()) {
|
||||
$account->{EXPIRY} = gettimeofday() + $expiryTime;
|
||||
}
|
||||
} elsif ($account->{STATE} eq 'inactive' && $modeName ne 'disarmed') {
|
||||
Arlo_Login($account);
|
||||
}
|
||||
}
|
||||
|
||||
sub Arlo_CheckBasestationsInactive() {
|
||||
my %defptr = %{$modules{$MODULE}{defptr}};
|
||||
foreach my $key (keys %defptr) {
|
||||
if (substr($key, 0, 1) eq 'B') {
|
||||
my $state = %defptr{$key}->{STATE};
|
||||
if ($state ne 'disarmed' && $state ne 'offline') {
|
||||
return \0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return \1;
|
||||
}
|
||||
|
||||
sub Arlo_BasestationArm($) {
|
||||
my ($hash) = @_;
|
||||
Arlo_DoSetBasestationMode($hash, 'mode1')
|
||||
@ -966,7 +1000,8 @@ sub Arlo_Login($) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hash->{STATE} = 'inactive';
|
||||
$hash->{STATE} = 'login';
|
||||
delete $hash->{EXPIRY};
|
||||
|
||||
my $password = encode_base64($hash->{helper}{password}, '');
|
||||
my $input = {email => $hash->{helper}{username}, password => $password, EnvSource => 'prod', language => 'de'};
|
||||
@ -979,51 +1014,51 @@ sub Arlo_Login($) {
|
||||
my $resp = $ua->request($req);
|
||||
|
||||
if ($resp->is_success) {
|
||||
my $loginPhase = 'Authenticate';
|
||||
eval {
|
||||
my $respObj = decode_json $resp->decoded_content;
|
||||
if ($respObj->{meta}{code} == 200) {
|
||||
$loginPhase = 'ValidateAccessToken';
|
||||
my $data = $respObj->{data};
|
||||
$hash->{helper}{token} = $data->{token};
|
||||
$hash->{helper}{userId} = $data->{userId};
|
||||
my $validateData = $data->{authenticated};
|
||||
my $authorization = encode_base64($data->{token}, '');
|
||||
$header = ['Content-Type' => 'application/json; charset=utf-8', 'Auth-Version' => 2, 'Authorization' => $authorization];
|
||||
$req = HTTP::Request->new('GET', 'https://ocapi-app.arlo.com/api/validateAccessToken?data='.$validateData, $header);
|
||||
my $validateData = $data->{authenticated};
|
||||
my $authorization = encode_base64($data->{token}, '');
|
||||
$header = ['Content-Type' => 'application/json; charset=utf-8', 'Auth-Version' => 2, 'Authorization' => $authorization];
|
||||
$req = HTTP::Request->new('GET', 'https://ocapi-app.arlo.com/api/validateAccessToken?data='.$validateData, $header);
|
||||
$resp = $ua->request($req);
|
||||
if ($resp->is_success) {
|
||||
$respObj = decode_json $resp->decoded_content;
|
||||
if ($resp->is_success) {
|
||||
$respObj = decode_json $resp->decoded_content;
|
||||
if ($respObj->{meta}{code} == 200) {
|
||||
$header = ['Content-Type' => 'application/json; charset=utf-8', 'Auth-Version' => 2, 'Authorization' => $data->{token}];
|
||||
$req = HTTP::Request->new('GET', 'https://my.arlo.com/hmsweb/users/session/v2', $header);
|
||||
$loginPhase = 'GetSession';
|
||||
$header = ['Content-Type' => 'application/json; charset=utf-8', 'Auth-Version' => 2, 'Authorization' => $data->{token}];
|
||||
$req = HTTP::Request->new('GET', 'https://my.arlo.com/hmsweb/users/session/v2', $header);
|
||||
$resp = $ua->request($req);
|
||||
if ($resp->is_success) {
|
||||
$respObj = decode_json $resp->decoded_content;
|
||||
if ($resp->is_success) {
|
||||
$respObj = decode_json $resp->decoded_content;
|
||||
if ($respObj->{success}) {
|
||||
$cookie_jar->extract_cookies($resp);
|
||||
$hash->{helper}{cookies} = Arlo_GetCookies($cookie_jar);
|
||||
Log3 $name, 5, $hash->{helper}{cookies};
|
||||
$hash->{SSE_STATUS} = 200;
|
||||
delete $hash->{RETRY};
|
||||
$hash->{STATE} = 'active';
|
||||
Arlo_Request($hash, '/users/devices');
|
||||
Arlo_EventQueue($hash);
|
||||
Arlo_Ping($hash);
|
||||
if (!defined($hash->{MODES})) {
|
||||
InternalTimer(gettimeofday() + 5, "Arlo_ReadModes", $hash);
|
||||
}
|
||||
InternalTimer(gettimeofday() + 30, "Arlo_Poll", $hash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$cookie_jar->extract_cookies($resp);
|
||||
$hash->{helper}{cookies} = Arlo_GetCookies($cookie_jar);
|
||||
Log3 $name, 5, $hash->{helper}{cookies};
|
||||
$hash->{SSE_STATUS} = 200;
|
||||
delete $hash->{RETRY};
|
||||
$hash->{STATE} = 'active';
|
||||
Arlo_Request($hash, '/users/devices');
|
||||
Arlo_EventQueue($hash);
|
||||
Arlo_Ping($hash);
|
||||
if (!defined($hash->{MODES})) {
|
||||
InternalTimer(gettimeofday() + 5, "Arlo_ReadModes", $hash);
|
||||
}
|
||||
InternalTimer(gettimeofday() + 30, "Arlo_Poll", $hash);
|
||||
$loginPhase = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log3 $name, 2, 'Arlo ValidateAccessToken not successful: '.$resp->decoded_content;
|
||||
} else {
|
||||
Log3 $name, 2, 'Arlo Login not successful: '.$resp->decoded_content;
|
||||
}
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
Log3 $hash->{NAME}, 2, 'Invalid Arlo response for login request: '.$resp->decoded_content;
|
||||
if ($@ || $loginPhase ne '') {
|
||||
Log3 $name, 2, 'Invalid Arlo response for login request during phase '.$loginPhase.': '.$resp->decoded_content;
|
||||
}
|
||||
} else {
|
||||
my $status_line = $resp->status_line;
|
||||
@ -1042,7 +1077,10 @@ sub Arlo_Login($) {
|
||||
|
||||
sub Arlo_Logout($) {
|
||||
my ($hash) = @_;
|
||||
RemoveInternalTimer($hash);
|
||||
delete $hash->{EXPIRY};
|
||||
Arlo_Request($hash, '/logout', 'PUT');
|
||||
$hash->{STATE} = 'inactive';
|
||||
}
|
||||
|
||||
sub Arlo_GetCookies($) {
|
||||
@ -1116,21 +1154,35 @@ sub Arlo_EventPolling($) {
|
||||
$nfound = select($rout=$rin, undef, undef, 0.1);
|
||||
}
|
||||
Arlo_ProcessResponse($hash, $content) if ($content ne '');
|
||||
if ($hash->{SSE_STATUS} == 299) {
|
||||
$hash->{RETRY} = 1;
|
||||
InternalTimer(gettimeofday() + 60, "Arlo_Login", $hash);
|
||||
|
||||
my $expiry = $hash->{EXPIRY};
|
||||
my $timeout = $hash->{RESPONSE_TIMEOUT};
|
||||
my $sseStatus = $hash->{SSE_STATUS};
|
||||
|
||||
if (defined($expiry)) {
|
||||
if ($expiry < gettimeofday() || $sseStatus == 299 || (defined($timeout) && $timeout < gettimeofday())) {
|
||||
Log3 $name, 3, "Arlo set to inactive.";
|
||||
HttpUtils_Close($con);
|
||||
Arlo_Logout($hash);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
my $timeout = $hash->{RESPONSE_TIMEOUT};
|
||||
if (defined($timeout) && $timeout < gettimeofday()) {
|
||||
$hash->{SSE_STATUS} = 0;
|
||||
Log3 $name, 3, "Arlo connection timeout, try to restart event listener.";
|
||||
HttpUtils_Close($con);
|
||||
Arlo_EventQueue($hash);
|
||||
if ($sseStatus == 299) {
|
||||
$hash->{RETRY} = 1;
|
||||
InternalTimer(gettimeofday() + 60, "Arlo_Login", $hash);
|
||||
return;
|
||||
} else {
|
||||
my $ssePollingInterval = AttrVal($name, 'ssePollingInterval', 2);
|
||||
InternalTimer(gettimeofday() + $ssePollingInterval, "Arlo_EventPolling", $hash);
|
||||
if (defined($timeout) && $timeout < gettimeofday()) {
|
||||
$hash->{SSE_STATUS} = 0;
|
||||
Log3 $name, 3, "Arlo connection timeout, try to restart event listener.";
|
||||
HttpUtils_Close($con);
|
||||
Arlo_EventQueue($hash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
my $ssePollingInterval = AttrVal($name, 'ssePollingInterval', 2);
|
||||
InternalTimer(gettimeofday() + $ssePollingInterval, "Arlo_EventPolling", $hash);
|
||||
}
|
||||
|
||||
|
||||
@ -1176,7 +1228,7 @@ sub Arlo_ProcessResponse($$) {
|
||||
}
|
||||
} elsif ($check ne 'event' && $check ne 'Cache' && $check ne 'Conte' && $check ne 'Date:' && $check ne 'Pragm' && $check ne 'Server'
|
||||
&& substr($check, 0, 2) ne 'X-' && $check ne 'trans' && $check ne 'Serve' && $check ne 'Expir' && $check ne 'Stric' && $check ne 'Trans'
|
||||
&& $check ne 'Expec' && $check ne 'CF-RA') {
|
||||
&& $check ne 'Expec' && $check ne 'CF-RA' && $check ne 'CF-Ca') {
|
||||
Log3 $hash->{NAME}, 2, "Invalid Arlo event response: $line";
|
||||
}
|
||||
}
|
||||
@ -1446,6 +1498,12 @@ sub Arlo_decrypt($) {
|
||||
<p>Subtype BASESTATION: Deactivates the periodic update of the readings from Arlo Cloud.</p>
|
||||
</ul>
|
||||
|
||||
<p><a name="ArloExpiryTime"></a> <b>expiryTime</b></p>
|
||||
<ul>
|
||||
<p>Subtype ACCOUNT: If all base stations have the status "disarmed" the connection to the cloud will be closed after this time. A new connection will be established if needed.
|
||||
Unit is seconds, default 600 (10 minutes). If you set the value to 0 the connection will not be closed.</p>
|
||||
</ul>
|
||||
|
||||
<p><a name="ArloPingVideoDownloadFix"></a> <b>videoDownloadFix</b></p>
|
||||
<ul>
|
||||
<p>Subtype ACCOUNT: Set this attribute to 1 if videos are not downloaded automatically. Normally the server sents a notification when there is a new video available but sometimes
|
||||
@ -1601,6 +1659,12 @@ sub Arlo_decrypt($) {
|
||||
<p>Subtype BASESTATION: Deaktiviert die regelmäßige Abfrage der Readings aus der Arlo Cloud.</p>
|
||||
</ul>
|
||||
|
||||
<p><a name="ArloExpiryTime"></a> <b>expiryTime</b></p>
|
||||
<ul>
|
||||
<p>Subtype ACCOUNT: Wenn alle Basisstation auf "disarmed" stehen, wird die Verbindung zur Cloud nach der hier angegebenen Zeit beendet. Bei einer Aktion mit einem Arlo-Gerät wird eine neue Verbindung aufgebaut.
|
||||
Angabe in Sekunden, Standard ist 600 (10 Minuten). Durch Angabe von 0 kann die Verbindung dauerhaft bestehen bleiben.</p>
|
||||
</ul>
|
||||
|
||||
<p><a name="ArloPingVideoDownloadFix"></a> <b>videoDownloadFix</b></p>
|
||||
<ul>
|
||||
<p>Subtype ACCOUNT: Dieser Wert muss auf 1 gesetzt werden, falls Videos nach der Aufnahme nicht automatisch heruntergeladen werden. Normalerweise werden Events vom Server gesendet,
|
||||
|
Loading…
x
Reference in New Issue
Block a user