Repository: fronzbot/blinkpy

Issue:
Trouble working with 0.16-RC0 - New Login Methods
Hi, I have the new RC up and running but seem to be having trouble getting it to work.  I setup a new routine to basically get and generate a new credential file:

from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth
from blinkpy.helpers.util import json_load

blink = Blink()
#auth = Auth(json_load("c:\Blinkpy\cred.json"))
auth = Auth({"username":"MY EMAIL", "password":"MY PASS"}, no_prompt=False)
blink.auth = auth
blink.start()
blink.save("C:\Blinkpy\cred.json")

On first run it works, I get a verification code, plug it in and my cred.json file is generated.

Then I go to run another method to enable/disable a bunch of cameras, like:
from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth
from blinkpy.helpers.util import json_load
import time

blink = Blink()
auth = Auth(json_load("c:\Blinkpy\cred.json"))
blink.auth = auth
blink.start()
blink.save("C:\Blinkpy\cred.json") <- I also tried to save the cred file at the end, which way shoudl I do it?

blink.cameras["Garage"].set_motion_detect(enable=False)
time.sleep(4)
blink.cameras["Mud Room"].set_motion_detect(enable=False)
time.sleep(4)
blink.cameras["Kitchen"].set_motion_detect(enable=False)
time.sleep(4)
blink.cameras["Dining"].set_motion_detect(enable=False)
time.sleep(4)
... A bunch of other cameras.

It may work once but eventually fails on blink.start or on one of the camera commands to disable motion with:
C:\Blinkpy>python disable_inside.py
Traceback (most recent call last):
  File "disable_inside.py", line 9, in <module>
    blink.start()
  File "C:\Python38\lib\site-packages\blinkpy\blinkpy.py", line 109, in start
    return self.setup_post_verify()
  File "C:\Python38\lib\site-packages\blinkpy\blinkpy.py", line 121, in setup_post_verify
    self.setup_networks()
  File "C:\Python38\lib\site-packages\blinkpy\blinkpy.py", line 176, in setup_networks
    response = api.request_networks(self)
  File "C:\Python38\lib\site-packages\blinkpy\api.py", line 62, in request_networks
    return http_get(blink, url)
  File "C:\Python38\lib\site-packages\blinkpy\api.py", line 272, in http_get
    return blink.auth.query(
  File "C:\Python38\lib\site-packages\blinkpy\auth.py", line 147, in query
    return self.validate_response(response, json_resp)
  File "C:\Python38\lib\site-packages\blinkpy\auth.py", line 114, in validate_response
    json_data = response.json()
  File "C:\Python38\lib\site-packages\requests\models.py", line 898, in json
    return complexjson.loads(self.text, **kwargs)
  File "C:\Python38\lib\json\__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "C:\Python38\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Python38\lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Then if I try to regenerate my cred file it will fail with "Cannot setup Blink platform", if I wait a while it eventually works then the process starts all over again, I get a good run, then it fails mid-stream, then I have to wait a while and regen my cred file.

Am I doing something wrong?  Should I generate a cred file first (asking for the code from Blink), save the cred file, then reference it in my future runs?  Also should I be resaving my cred file after each run?

Thanks for the help!

Comments:

Patch:
diff --git a/blinkpy/auth.py b/blinkpy/auth.py
index 778daf3..2f07048 100644
--- a/blinkpy/auth.py
+++ b/blinkpy/auth.py
@@ -111,12 +111,14 @@ class Auth:
         if not json_resp:
             return response
 
-        json_data = response.json()
         try:
+            json_data = response.json()
             if json_data["code"] in ERROR.BLINK_ERRORS:
                 raise exceptions.ConnectionError
         except KeyError:
             pass
+        except (AttributeError, ValueError):
+            raise BlinkBadResponse
 
         return json_data
 
@@ -161,6 +163,8 @@ class Auth:
                     )
             except (TokenRefreshFailed, LoginError):
                 _LOGGER.error("Endpoint %s failed. Unable to refresh login tokens", url)
+        except BlinkBadResponse:
+            _LOGGER.error("Expected json response, but received: %s", response)
         _LOGGER.error("Endpoint %s failed", url)
         return None
 
@@ -192,3 +196,7 @@ class TokenRefreshFailed(Exception):
 
 class LoginError(Exception):
     """Class to throw failed login exception."""
+
+
+class BlinkBadResponse(Exception):
+    """Class to throw bad json response exception."""


Test patch:
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 88f9d7d..8086267 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -3,7 +3,7 @@
 import unittest
 from unittest import mock
 from requests import exceptions
-from blinkpy.auth import Auth, LoginError, TokenRefreshFailed
+from blinkpy.auth import Auth, LoginError, TokenRefreshFailed, BlinkBadResponse
 import blinkpy.helpers.constants as const
 import tests.mock_responses as mresp
 
@@ -100,6 +100,11 @@ class TestAuth(unittest.TestCase):
         fake_resp = "foobar"
         self.assertEqual(self.auth.validate_response(fake_resp, False), "foobar")
 
+    def test_response_bad_json(self):
+        """Check response when not json but expecting json."""
+        with self.assertRaises(BlinkBadResponse):
+            self.auth.validate_response(None, True)
+
     def test_header(self):
         """Test header data."""
         self.auth.token = "bar"