Repository: fronzbot/blinkpy

Issue:
Exception on blink.refresh
I'm attempting to automate capture from a Blink Mini camera but the `blink.refresh()` command always produces the following error:

```
blink.refresh()
  File "/Users/lakario/Library/Python/3.8/lib/python/site-packages/blinkpy/helpers/util.py", line 144, in wrapper
    result = method(*args, *kwargs)
  File "/Users/lakario/Library/Python/3.8/lib/python/site-packages/blinkpy/blinkpy.py", line 92, in refresh
    sync_module.refresh(force_cache=force)
  File "/Users/lakario/Library/Python/3.8/lib/python/site-packages/blinkpy/sync_module.py", line 193, in refresh
    camera_id, owl_info=self.get_owl_info(camera_name)
  File "/Users/lakario/Library/Python/3.8/lib/python/site-packages/blinkpy/sync_module.py", line 145, in get_owl_info
    for owl in self.blink.homescreen["owls"]:
TypeError: 'NoneType' object is not subscriptable
```

I worked my way back through the code and I was able to successfully refresh my "owl" (Mini camera) with the internal calls. It's not obvious why it doesn't work with the `blink.refresh()`. 

```
camera_info=blink.sync['Home'].get_owl_info('Farm')
camera = blink.cameras['Farm']
camera.snap_picture()
blink.sync['Home'].cameras['Farm'].update(camera_info, force_cache=True)
```

Let me know if I can provide any extra info which will help.

Discussion:

Patch:
diff --git a/blinkpy/blinkpy.py b/blinkpy/blinkpy.py
index 70abd8d..8205458 100644
--- a/blinkpy/blinkpy.py
+++ b/blinkpy/blinkpy.py
@@ -76,21 +76,22 @@ class Blink:
         self.no_owls = no_owls
 
     @util.Throttle(seconds=MIN_THROTTLE_TIME)
-    def refresh(self, force=False):
+    def refresh(self, force=False, force_cache=False):
         """
         Perform a system refresh.
 
-        :param force: Force an update of the camera data
+        :param force: Used to override throttle, resets refresh
+        :param force_cache: Used to force update without overriding throttle
         """
-        if self.check_if_ok_to_update() or force:
+        if self.check_if_ok_to_update() or force or force_cache:
             if not self.available:
                 self.setup_post_verify()
 
             self.get_homescreen()
             for sync_name, sync_module in self.sync.items():
                 _LOGGER.debug("Attempting refresh of sync %s", sync_name)
-                sync_module.refresh(force_cache=force)
-            if not force:
+                sync_module.refresh(force_cache=(force or force_cache))
+            if not force_cache:
                 # Prevents rapid clearing of motion detect property
                 self.last_refresh = int(time.time())
             return True
diff --git a/blinkpy/sync_module.py b/blinkpy/sync_module.py
index ad780a1..4e31756 100644
--- a/blinkpy/sync_module.py
+++ b/blinkpy/sync_module.py
@@ -145,7 +145,7 @@ class BlinkSyncModule:
             for owl in self.blink.homescreen["owls"]:
                 if owl["name"] == name:
                     return owl
-        except KeyError:
+        except (TypeError, KeyError):
             pass
         return None
 
@@ -270,7 +270,7 @@ class BlinkOwl(BlinkSyncModule):
                 if owl["name"] == self.name:
                     self.status = owl["enabled"]
                     return owl
-        except KeyError:
+        except (TypeError, KeyError):
             pass
         return None
 


Test patch:
diff --git a/tests/test_blink_functions.py b/tests/test_blink_functions.py
index 89a45dc..8a71294 100644
--- a/tests/test_blink_functions.py
+++ b/tests/test_blink_functions.py
@@ -5,29 +5,23 @@ import logging
 
 from blinkpy import blinkpy
 from blinkpy.sync_module import BlinkSyncModule
+from blinkpy.camera import BlinkCamera
 from blinkpy.helpers.util import get_time, BlinkURLHandler
 
 
 class MockSyncModule(BlinkSyncModule):
-    """Mock http requests from sync module."""
+    """Mock blink sync module object."""
 
-    def __init__(self, blink, header):
-        """Create mock sync module instance."""
-        super().__init__(blink, header, network_id=None, camera_list=None)
-        self.blink = blink
-        self.header = header
-        self.return_value = None
-        self.return_value2 = None
+    def get_network_info(self):
+        """Mock network info method."""
+        return True
 
-    def http_get(self, url, stream=False, json=True):
-        """Mock get request."""
-        if stream and self.return_value2 is not None:
-            return self.return_value2
-        return self.return_value
 
-    def http_post(self, url):
-        """Mock post request."""
-        return self.return_value
+class MockCamera(BlinkCamera):
+    """Mock blink camera object."""
+
+    def update(self, config, force_cache=False, **kwargs):
+        """Mock camera update method."""
 
 
 class TestBlinkFunctions(unittest.TestCase):
@@ -121,3 +115,16 @@ class TestBlinkFunctions(unittest.TestCase):
         with self.assertLogs() as dl_log:
             blink.download_videos("/tmp", camera="bar", stop=2)
         self.assertEqual(dl_log.output, expected_log)
+
+    @mock.patch("blinkpy.blinkpy.api.request_network_update")
+    @mock.patch("blinkpy.auth.Auth.query")
+    def test_refresh(self, mock_req, mock_update):
+        """Test ability to refresh system."""
+        mock_update.return_value = {"network": {"sync_module_error": False}}
+        mock_req.return_value = None
+        self.blink.last_refresh = 0
+        self.blink.available = True
+        self.blink.sync["foo"] = MockSyncModule(self.blink, "foo", 1, [])
+        self.blink.cameras = {"bar": MockCamera(self.blink.sync)}
+        self.blink.sync["foo"].cameras = self.blink.cameras
+        self.assertTrue(self.blink.refresh())
diff --git a/tests/test_blinkpy.py b/tests/test_blinkpy.py
index aed2edb..c632215 100644
--- a/tests/test_blinkpy.py
+++ b/tests/test_blinkpy.py
@@ -69,7 +69,7 @@ class TestBlinkSetup(unittest.TestCase):
         with mock.patch(
             "blinkpy.sync_module.BlinkSyncModule.refresh", return_value=True
         ), mock.patch("blinkpy.blinkpy.Blink.get_homescreen", return_value=True):
-            self.blink.refresh()
+            self.blink.refresh(force=True)
 
         self.assertEqual(self.blink.last_refresh, now)
         self.assertEqual(self.blink.check_if_ok_to_update(), False)