Updated login method to allow for email verification and 2FA in future
This commit is contained in:
@@ -36,6 +36,7 @@ def request_login(
|
||||
"client_name": "Computer",
|
||||
"client_type": "android",
|
||||
"device_identifier": "Blinkpy",
|
||||
"device_name": "Blinkpy",
|
||||
"os_version": "5.1.1",
|
||||
"reauth": "true",
|
||||
}
|
||||
|
||||
+36
-18
@@ -57,6 +57,7 @@ class Blink:
|
||||
motion_interval=DEFAULT_MOTION_INTERVAL,
|
||||
legacy_subdomain=False,
|
||||
no_prompt=False,
|
||||
persist_key=None,
|
||||
):
|
||||
"""
|
||||
Initialize Blink system.
|
||||
@@ -78,9 +79,13 @@ class Blink:
|
||||
api issues).
|
||||
:param no_prompt: Set to TRUE if using an implementation that needs to
|
||||
suppress command-line output.
|
||||
:param persist_key: Location of persistant identifier.
|
||||
"""
|
||||
self.login_handler = LoginHandler(
|
||||
username=username, password=password, cred_file=cred_file
|
||||
username=username,
|
||||
password=password,
|
||||
cred_file=cred_file,
|
||||
persist_key=persist_key,
|
||||
)
|
||||
self._token = None
|
||||
self._auth_header = None
|
||||
@@ -126,24 +131,29 @@ class Blink:
|
||||
if self.key_required and not self.no_prompt:
|
||||
email = self.login_handler.data["username"]
|
||||
key = input("Enter code sent to {}: ".format(email))
|
||||
self.login_handler.send_auth_key(self, key)
|
||||
result = self.login_handler.send_auth_key(self, key)
|
||||
self.key_required = not result
|
||||
self.setup_post_verify()
|
||||
elif not self.key_required:
|
||||
self.setup_post_verify()
|
||||
|
||||
if self.available:
|
||||
camera_list = self.get_cameras()
|
||||
networks = self.get_ids()
|
||||
for network_name, network_id in networks.items():
|
||||
if network_id not in camera_list.keys():
|
||||
camera_list[network_id] = {}
|
||||
_LOGGER.warning("No cameras found for %s", network_name)
|
||||
sync_module = BlinkSyncModule(
|
||||
self, network_name, network_id, camera_list[network_id]
|
||||
)
|
||||
sync_module.start()
|
||||
self.sync[network_name] = sync_module
|
||||
def setup_post_verify(self):
|
||||
"""Initialize blink system after verification."""
|
||||
camera_list = self.get_cameras()
|
||||
networks = self.get_ids()
|
||||
for network_name, network_id in networks.items():
|
||||
if network_id not in camera_list.keys():
|
||||
camera_list[network_id] = {}
|
||||
_LOGGER.warning("No cameras found for %s", network_name)
|
||||
sync_module = BlinkSyncModule(
|
||||
self, network_name, network_id, camera_list[network_id]
|
||||
)
|
||||
sync_module.start()
|
||||
self.sync[network_name] = sync_module
|
||||
self.cameras = self.merge_cameras()
|
||||
|
||||
def login(self):
|
||||
"""Login method. DEPRECATED."""
|
||||
"""Perform server login. DEPRECATED."""
|
||||
_LOGGER.warning(
|
||||
"Method is deprecated and will be removed in a future version. Please use the LoginHandler.login() method instead."
|
||||
)
|
||||
@@ -165,11 +175,19 @@ class Blink:
|
||||
((self.region_id, self.region),) = response["region"].items()
|
||||
self._host = "{}.{}".format(self.region_id, BLINK_URL)
|
||||
self._token = response["authtoken"]["authtoken"]
|
||||
self.networks = response["networks"]
|
||||
self.client_id = response["client"]["id"]
|
||||
self.account_id = response["account"]["id"]
|
||||
self._auth_header = {"Host": self._host, "TOKEN_AUTH": self._token}
|
||||
self.urls = BlinkURLHandler(self.region_id, legacy=self.legacy)
|
||||
self.networks = self.get_networks()
|
||||
self.client_id = response["client"]["id"]
|
||||
self.account_id = response["account"]["id"]
|
||||
|
||||
def get_networks(self):
|
||||
"""Get network information."""
|
||||
response = api.request_networks(self)
|
||||
try:
|
||||
return response["summary"]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def get_ids(self):
|
||||
"""Set the network ID and Account ID."""
|
||||
|
||||
@@ -50,6 +50,7 @@ DEFAULT_URL = "{}.{}".format("rest-prod", BLINK_URL)
|
||||
BASE_URL = "https://{}".format(DEFAULT_URL)
|
||||
LOGIN_URLS = [
|
||||
"{}/api/v4/login".format(BASE_URL),
|
||||
"{}/api/v3/login".format(BASE_URL),
|
||||
"{}/api/v2/login".format(BASE_URL),
|
||||
]
|
||||
|
||||
|
||||
+25
-11
@@ -13,26 +13,43 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class LoginHandler:
|
||||
"""Class to handle login communication."""
|
||||
|
||||
def __init__(
|
||||
self, username=None, password=None, cred_file=None,
|
||||
):
|
||||
def __init__(self, username=None, password=None, cred_file=None, persist_key=None):
|
||||
"""
|
||||
Initialize login handler.
|
||||
|
||||
:param username: Blink username
|
||||
:param password: Blink password
|
||||
:param cred_file: JSON formatted credential file.
|
||||
:param persist_key: File location of persistant key.
|
||||
"""
|
||||
self.login_url = None
|
||||
self.login_urls = const.LOGIN_URLS
|
||||
self.cred_file = cred_file
|
||||
self.persist_key = persist_key
|
||||
self.data = {
|
||||
"username": username,
|
||||
"password": password,
|
||||
"uid": util.gen_uid(const.SIZE_UID),
|
||||
"notification_key": util.gen_uid(const.SIZE_NOTIFICATION_KEY),
|
||||
"uid": None,
|
||||
"notification_key": None,
|
||||
}
|
||||
|
||||
self.check_keys()
|
||||
|
||||
def check_keys(self):
|
||||
"""Check if uid exists, if not create."""
|
||||
uid = util.gen_uid(const.SIZE_UID)
|
||||
notification_key = util.gen_uid(const.SIZE_NOTIFICATION_KEY)
|
||||
data = {"uid": uid, "notification_key": notification_key}
|
||||
if self.persist_key is None:
|
||||
return data
|
||||
if not isfile(self.persist_key):
|
||||
with open(self.persist_key, "w") as json_file:
|
||||
json.dump(data, json_file)
|
||||
else:
|
||||
with open(self.persist_key, "r") as json_file:
|
||||
data = json.load(json_file)
|
||||
return data
|
||||
|
||||
def check_cred_file(self):
|
||||
"""Check if credential file supplied and use if so."""
|
||||
if isfile(self.cred_file):
|
||||
@@ -113,18 +130,15 @@ class LoginHandler:
|
||||
json_resp = response.json()
|
||||
blink.available = json_resp["valid"]
|
||||
except KeyError:
|
||||
blink.available = False
|
||||
return False
|
||||
_LOGGER.error("Did not receive valid response from server.")
|
||||
_LOGGER.error("Invalid key. Got %s", key)
|
||||
return True
|
||||
|
||||
def check_key_required(self, blink):
|
||||
"""Check if 2FA key is required."""
|
||||
# No idea if this is the right end point. Placeholder for now.
|
||||
try:
|
||||
if blink.login_response["account"]["email_verification_required"]:
|
||||
blink.available = False
|
||||
if blink.login_response["client"]["verification_required"]:
|
||||
return True
|
||||
except KeyError:
|
||||
pass
|
||||
blink.available = True
|
||||
return False
|
||||
|
||||
+16
-3
@@ -38,6 +38,7 @@ class BlinkSyncModule:
|
||||
self.motion = {}
|
||||
self.last_record = {}
|
||||
self.camera_list = camera_list
|
||||
self.available = False
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
@@ -102,8 +103,7 @@ class BlinkSyncModule:
|
||||
"Could not extract some sync module info: %s", response, exc_info=True
|
||||
)
|
||||
|
||||
self.network_info = api.request_network_status(self.blink, self.network_id)
|
||||
|
||||
self.get_network_info()
|
||||
self.check_new_videos()
|
||||
try:
|
||||
for camera_config in self.camera_list:
|
||||
@@ -120,6 +120,7 @@ class BlinkSyncModule:
|
||||
)
|
||||
return False
|
||||
|
||||
self.available = True
|
||||
return True
|
||||
|
||||
def get_events(self, **kwargs):
|
||||
@@ -141,9 +142,21 @@ class BlinkSyncModule:
|
||||
_LOGGER.error("Could not extract camera info: %s", response, exc_info=True)
|
||||
return []
|
||||
|
||||
def get_network_info(self):
|
||||
"""Retrieve network status."""
|
||||
is_errored = False
|
||||
self.network_info = api.request_network_status(self.blink, self.network_id)
|
||||
try:
|
||||
is_errored = self.network_info["network"]["sync_module_error"]
|
||||
except KeyError:
|
||||
is_errored = True
|
||||
|
||||
if is_errored:
|
||||
self.available = False
|
||||
|
||||
def refresh(self, force_cache=False):
|
||||
"""Get all blink cameras and pulls their most recent status."""
|
||||
self.network_info = api.request_network_status(self.blink, self.network_id)
|
||||
self.get_network_info()
|
||||
self.check_new_videos()
|
||||
for camera_name in self.cameras.keys():
|
||||
camera_id = self.cameras[camera_name].camera_id
|
||||
|
||||
Reference in New Issue
Block a user