Repository: fronzbot/blinkpy

Issue:
Add logout endpoint
**Is your feature request related to a problem? Please describe.**
If region info or anything gets stale, would be nice to be able to logout and log back in via the library

**Describe the solution you'd like**
Add the logout endpoint: https://github.com/MattTW/BlinkMonitorProtocol/blob/master/auth/logout.md



Discussion:

Patch:
diff --git a/blinkpy/api.py b/blinkpy/api.py
index 98fdb4f..0d77646 100644
--- a/blinkpy/api.py
+++ b/blinkpy/api.py
@@ -54,6 +54,12 @@ def request_verify(auth, blink, verify_key):
     )
 
 
+def request_logout(blink):
+    """Logout of blink servers."""
+    url = f"{blink.urls.base_url}/api/v4/account/{blink.account_id}/client/{blink.client_id}/logout"
+    return http_post(blink, url=url)
+
+
 def request_networks(blink):
     """Request all networks information."""
     url = f"{blink.urls.base_url}/networks"
diff --git a/blinkpy/auth.py b/blinkpy/auth.py
index 55a4a1c..3e78c13 100644
--- a/blinkpy/auth.py
+++ b/blinkpy/auth.py
@@ -108,8 +108,12 @@ class Auth:
             if response.status_code == 200:
                 return response.json()
             raise LoginError
-        except AttributeError:
-            raise LoginError
+        except AttributeError as error:
+            raise LoginError from error
+
+    def logout(self, blink):
+        """Log out."""
+        return api.request_logout(blink)
 
     def refresh_token(self):
         """Refresh auth token."""
@@ -119,12 +123,12 @@ class Auth:
             self.login_response = self.login()
             self.extract_login_info()
             self.is_errored = False
-        except LoginError:
+        except LoginError as error:
             _LOGGER.error("Login endpoint failed. Try again later.")
-            raise TokenRefreshFailed
-        except (TypeError, KeyError):
+            raise TokenRefreshFailed from error
+        except (TypeError, KeyError) as error:
             _LOGGER.error("Malformed login response: %s", self.login_response)
-            raise TokenRefreshFailed
+            raise TokenRefreshFailed from error
         return True
 
     def extract_login_info(self):
@@ -155,8 +159,8 @@ class Auth:
             json_data = response.json()
         except KeyError:
             pass
-        except (AttributeError, ValueError):
-            raise BlinkBadResponse
+        except (AttributeError, ValueError) as error:
+            raise BlinkBadResponse from error
 
         self.is_errored = False
         return json_data
@@ -232,7 +236,7 @@ class Auth:
                 json_resp = response.json()
                 blink.available = json_resp["valid"]
                 if not json_resp["valid"]:
-                    _LOGGER.error(f"{json_resp['message']}")
+                    _LOGGER.error("%s", json_resp["message"])
                     return False
             except (KeyError, TypeError):
                 _LOGGER.error("Did not receive valid response from server.")


Test patch:
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 3699d8a..9c91897 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -192,6 +192,13 @@ class TestAuth(unittest.TestCase):
         self.auth.login_response = {"account": {"client_verification_required": True}}
         self.assertTrue(self.auth.check_key_required())
 
+    @mock.patch("blinkpy.auth.api.request_logout")
+    def test_logout(self, mock_req):
+        """Test logout method."""
+        mock_blink = MockBlink(None)
+        mock_req.return_value = True
+        self.assertTrue(self.auth.logout(mock_blink))
+
     @mock.patch("blinkpy.auth.api.request_verify")
     def test_send_auth_key(self, mock_req):
         """Check sending of auth key."""