def blink_video_schedule(event, context): """Triggered from a message on a Cloud Pub/Sub topic. Args: event (dict): Event payload. context (google.cloud.functions.Context): Metadata for the event.""" FILENAME = 'blink_creds.json' #FILENAME = re.sub(r"\/.*\/(.*\.\w{1,4}",r'\1',FILE) #BLOB_UPLOAD = BLINK_BUCKET.blob(f"{create_file_path()[1:]}/{FILENAME}") #Set filename format (uploads/year/month/filename). #BLOB_UPLOAD.upload_from_filename(FILE) USER_NAME, PASSWORD = load_credentials() AUTH = Auth({"username": USER_NAME, "password": PASSWORD}, no_prompt=True) pubsub_message = base64.b64decode(event['data']).decode('utf-8') if pubsub_message == 'RUN': blink = Blink() blink.auth = AUTH blink.start() AUTH.send_auth_key(blink, '167363') blink.setup_post_verify() #print(type(blink.save(f'{FILENAME}'))) CREDS = json_load("blink_creds.json") blob_blink = BLINK_BUCKET.blob('blink_creds.json') blob_blink.upload_from_string(data=json.dumps(CREDS), content_type='application/json') print('i am before the cameras') print(blink.cameras.items()) try: for name, camera in blink.cameras.items(): print('i am inside the camera') print(name) # Name of the camera print( camera.attributes) # Print available attributes of camera except ValueError: print('there is some error') blink.download_videos(since='2018/07/04 09:34') return "SUCCESS"
class TestAuth(unittest.TestCase): """Test the Auth class in blinkpy.""" def setUp(self): """Set up Login Handler.""" self.auth = Auth() def tearDown(self): """Clean up after test.""" self.auth = None @mock.patch("blinkpy.helpers.util.gen_uid") @mock.patch("blinkpy.auth.util.getpass") def test_empty_init(self, getpwd, genuid): """Test initialization with no params.""" auth = Auth() self.assertDictEqual(auth.data, {}) getpwd.return_value = "bar" genuid.return_value = 1234 with mock.patch("builtins.input", return_value="foo"): auth.validate_login() expected_data = { "username": "******", "password": "******", "uid": 1234, "device_id": const.DEVICE_ID, } self.assertDictEqual(auth.data, expected_data) @mock.patch("blinkpy.helpers.util.gen_uid") @mock.patch("blinkpy.auth.util.getpass") def test_barebones_init(self, getpwd, genuid): """Test basebones initialization.""" login_data = {"username": "******", "password": "******"} auth = Auth(login_data) self.assertDictEqual(auth.data, login_data) getpwd.return_value = "bar" genuid.return_value = 1234 with mock.patch("builtins.input", return_value="foo"): auth.validate_login() expected_data = { "username": "******", "password": "******", "uid": 1234, "device_id": const.DEVICE_ID, } self.assertDictEqual(auth.data, expected_data) def test_full_init(self): """Test full initialization.""" login_data = { "username": "******", "password": "******", "token": "token", "host": "host", "region_id": "region_id", "client_id": "client_id", "account_id": "account_id", "uid": 1234, "notification_key": 4321, "device_id": "device_id", } auth = Auth(login_data) self.assertEqual(auth.token, "token") self.assertEqual(auth.host, "host") self.assertEqual(auth.region_id, "region_id") self.assertEqual(auth.client_id, "client_id") self.assertEqual(auth.account_id, "account_id") auth.validate_login() self.assertDictEqual(auth.login_attributes, login_data) def test_bad_response_code(self): """Check bad response code from server.""" self.auth.is_errored = False fake_resp = mresp.MockResponse({"code": 404}, 404) with self.assertRaises(exceptions.ConnectionError): self.auth.validate_response(fake_resp, True) self.assertTrue(self.auth.is_errored) self.auth.is_errored = False fake_resp = mresp.MockResponse({"code": 101}, 401) with self.assertRaises(UnauthorizedError): self.auth.validate_response(fake_resp, True) self.assertTrue(self.auth.is_errored) def test_good_response_code(self): """Check good response code from server.""" fake_resp = mresp.MockResponse({"foo": "bar"}, 200) self.auth.is_errored = True self.assertEqual(self.auth.validate_response(fake_resp, True), {"foo": "bar"}) self.assertFalse(self.auth.is_errored) def test_response_not_json(self): """Check response when not json.""" fake_resp = "foobar" self.auth.is_errored = True self.assertEqual(self.auth.validate_response(fake_resp, False), "foobar") self.assertFalse(self.auth.is_errored) def test_response_bad_json(self): """Check response when not json but expecting json.""" self.auth.is_errored = False with self.assertRaises(BlinkBadResponse): self.auth.validate_response(None, True) self.assertTrue(self.auth.is_errored) def test_header(self): """Test header data.""" self.auth.token = "bar" expected_header = { "TOKEN_AUTH": "bar", "user-agent": const.DEFAULT_USER_AGENT, "content-type": "application/json", } self.assertDictEqual(self.auth.header, expected_header) def test_header_no_token(self): """Test header without token.""" self.auth.token = None self.assertEqual(self.auth.header, None) @mock.patch("blinkpy.auth.Auth.validate_login", return_value=None) @mock.patch("blinkpy.auth.api.request_login") def test_login(self, mock_req, mock_validate): """Test login handling.""" fake_resp = mresp.MockResponse({"foo": "bar"}, 200) mock_req.return_value = fake_resp self.assertEqual(self.auth.login(), {"foo": "bar"}) @mock.patch("blinkpy.auth.Auth.validate_login", return_value=None) @mock.patch("blinkpy.auth.api.request_login") def test_login_bad_response(self, mock_req, mock_validate): """Test login handling when bad response.""" fake_resp = mresp.MockResponse({"foo": "bar"}, 404) mock_req.return_value = fake_resp self.auth.is_errored = False with self.assertRaises(LoginError): self.auth.login() with self.assertRaises(TokenRefreshFailed): self.auth.refresh_token() self.assertTrue(self.auth.is_errored) @mock.patch("blinkpy.auth.Auth.login") def test_refresh_token(self, mock_login): """Test refresh token method.""" mock_login.return_value = { "account": { "account_id": 5678, "client_id": 1234, "tier": "test" }, "auth": { "token": "foobar" }, } self.assertTrue(self.auth.refresh_token()) self.assertEqual(self.auth.region_id, "test") self.assertEqual(self.auth.token, "foobar") self.assertEqual(self.auth.client_id, 1234) self.assertEqual(self.auth.account_id, 5678) @mock.patch("blinkpy.auth.Auth.login") def test_refresh_token_failed(self, mock_login): """Test refresh token failed.""" mock_login.return_value = {} self.auth.is_errored = False with self.assertRaises(TokenRefreshFailed): self.auth.refresh_token() self.assertTrue(self.auth.is_errored) def test_check_key_required(self): """Check key required method.""" self.auth.login_response = {} self.assertFalse(self.auth.check_key_required()) self.auth.login_response = { "account": { "client_verification_required": False } } self.assertFalse(self.auth.check_key_required()) 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.""" mock_blink = MockBlink(None) mock_req.return_value = mresp.MockResponse({"valid": True}, 200) self.assertTrue(self.auth.send_auth_key(mock_blink, 1234)) self.assertTrue(mock_blink.available) mock_req.return_value = mresp.MockResponse(None, 200) self.assertFalse(self.auth.send_auth_key(mock_blink, 1234)) mock_req.return_value = mresp.MockResponse({}, 200) self.assertFalse(self.auth.send_auth_key(mock_blink, 1234)) self.assertTrue(self.auth.send_auth_key(mock_blink, None)) @mock.patch("blinkpy.auth.api.request_verify") def test_send_auth_key_fail(self, mock_req): """Check handling of auth key failure.""" mock_blink = MockBlink(None) mock_req.return_value = mresp.MockResponse(None, 200) self.assertFalse(self.auth.send_auth_key(mock_blink, 1234)) mock_req.return_value = mresp.MockResponse({}, 200) self.assertFalse(self.auth.send_auth_key(mock_blink, 1234)) @mock.patch("blinkpy.auth.Auth.validate_response") @mock.patch("blinkpy.auth.Auth.refresh_token") def test_query_retry(self, mock_refresh, mock_validate): """Check handling of request retry.""" self.auth.session = MockSession() mock_validate.side_effect = [UnauthorizedError, "foobar"] mock_refresh.return_value = True self.assertEqual(self.auth.query(url="http://example.com"), "foobar") @mock.patch("blinkpy.auth.Auth.validate_response") @mock.patch("blinkpy.auth.Auth.refresh_token") def test_query_retry_failed(self, mock_refresh, mock_validate): """Check handling of failed retry request.""" self.auth.session = MockSession() mock_validate.side_effect = [UnauthorizedError, BlinkBadResponse] mock_refresh.return_value = True self.assertEqual(self.auth.query(url="http://example.com"), None) mock_validate.side_effect = [UnauthorizedError, TokenRefreshFailed] self.assertEqual(self.auth.query(url="http://example.com"), None) def test_default_session(self): """Test default session creation.""" sess = self.auth.create_session() adapter = sess.adapters["https://"] self.assertEqual(adapter.max_retries.total, 3) self.assertEqual(adapter.max_retries.backoff_factor, 1) self.assertEqual(adapter.max_retries.status_forcelist, [429, 500, 502, 503, 504]) def test_custom_session_full(self): """Test full custom session creation.""" opts = {"backoff": 2, "retries": 10, "retry_list": [404]} sess = self.auth.create_session(opts=opts) adapter = sess.adapters["https://"] self.assertEqual(adapter.max_retries.total, 10) self.assertEqual(adapter.max_retries.backoff_factor, 2) self.assertEqual(adapter.max_retries.status_forcelist, [404]) def test_custom_session_partial(self): """Test partial custom session creation.""" opts1 = {"backoff": 2} opts2 = {"retries": 5} opts3 = {"retry_list": [101, 202]} sess1 = self.auth.create_session(opts=opts1) sess2 = self.auth.create_session(opts=opts2) sess3 = self.auth.create_session(opts=opts3) adapt1 = sess1.adapters["https://"] adapt2 = sess2.adapters["https://"] adapt3 = sess3.adapters["https://"] self.assertEqual(adapt1.max_retries.total, 3) self.assertEqual(adapt1.max_retries.backoff_factor, 2) self.assertEqual(adapt1.max_retries.status_forcelist, [429, 500, 502, 503, 504]) self.assertEqual(adapt2.max_retries.total, 5) self.assertEqual(adapt2.max_retries.backoff_factor, 1) self.assertEqual(adapt2.max_retries.status_forcelist, [429, 500, 502, 503, 504]) self.assertEqual(adapt3.max_retries.total, 3) self.assertEqual(adapt3.max_retries.backoff_factor, 1) self.assertEqual(adapt3.max_retries.status_forcelist, [101, 202])
class Blink: """Class to initialize communication.""" def __init__( self, refresh_rate=DEFAULT_REFRESH, motion_interval=DEFAULT_MOTION_INTERVAL, no_owls=False, ): """ Initialize Blink system. :param refresh_rate: Refresh rate of blink information. Defaults to 15 (seconds) :param motion_interval: How far back to register motion in minutes. Defaults to last refresh time. Useful for preventing motion_detected property from de-asserting too quickly. :param no_owls: Disable searching for owl entries (blink mini cameras only known entity). Prevents an uneccessary API call if you don't have these in your network. """ self.auth = Auth() self.account_id = None self.client_id = None self.network_ids = [] self.urls = None self.sync = CaseInsensitiveDict({}) self.last_refresh = None self.refresh_rate = refresh_rate self.networks = [] self.cameras = CaseInsensitiveDict({}) self.video_list = CaseInsensitiveDict({}) self.motion_interval = motion_interval self.version = __version__ self.available = False self.key_required = False self.homescreen = {} self.no_owls = no_owls @util.Throttle(seconds=MIN_THROTTLE_TIME) def refresh(self, force=False, force_cache=False): """ Perform a system refresh. :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 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 or force_cache)) if not force_cache: # Prevents rapid clearing of motion detect property self.last_refresh = int(time.time()) return True return False def start(self): """Perform full system setup.""" try: self.auth.startup() self.setup_login_ids() self.setup_urls() self.get_homescreen() except (LoginError, TokenRefreshFailed, BlinkSetupError): _LOGGER.error("Cannot setup Blink platform.") self.available = False return False self.key_required = self.auth.check_key_required() if self.key_required: if self.auth.no_prompt: return True self.setup_prompt_2fa() return self.setup_post_verify() def setup_prompt_2fa(self): """Prompt for 2FA.""" email = self.auth.data["username"] pin = input(f"Enter code sent to {email}: ") result = self.auth.send_auth_key(self, pin) self.key_required = not result def setup_post_verify(self): """Initialize blink system after verification.""" try: self.setup_networks() networks = self.setup_network_ids() cameras = self.setup_camera_list() except BlinkSetupError: self.available = False return False for name, network_id in networks.items(): sync_cameras = cameras.get(network_id, {}) self.setup_sync_module(name, network_id, sync_cameras) self.cameras = self.merge_cameras() self.available = True self.key_required = False return True def setup_sync_module(self, name, network_id, cameras): """Initialize a sync module.""" self.sync[name] = BlinkSyncModule(self, name, network_id, cameras) self.sync[name].start() def get_homescreen(self): """Get homecreen information.""" if self.no_owls: _LOGGER.debug("Skipping owl extraction.") self.homescreen = {} return self.homescreen = api.request_homescreen(self) def setup_owls(self): """Check for mini cameras.""" network_list = [] camera_list = [] try: for owl in self.homescreen["owls"]: name = owl["name"] network_id = str(owl["network_id"]) if network_id in self.network_ids: camera_list.append( {network_id: {"name": name, "id": network_id, "type": "mini"}} ) continue if owl["onboarded"]: network_list.append(str(network_id)) self.sync[name] = BlinkOwl(self, name, network_id, owl) self.sync[name].start() except KeyError: # No sync-less devices found pass self.network_ids.extend(network_list) return camera_list def setup_camera_list(self): """Create camera list for onboarded networks.""" all_cameras = {} response = api.request_camera_usage(self) try: for network in response["networks"]: camera_network = str(network["network_id"]) if camera_network not in all_cameras: all_cameras[camera_network] = [] for camera in network["cameras"]: all_cameras[camera_network].append( {"name": camera["name"], "id": camera["id"]} ) mini_cameras = self.setup_owls() for camera in mini_cameras: for network, camera_info in camera.items(): all_cameras[network].append(camera_info) return all_cameras except (KeyError, TypeError): _LOGGER.error("Unable to retrieve cameras from response %s", response) raise BlinkSetupError def setup_login_ids(self): """Retrieve login id numbers from login response.""" self.client_id = self.auth.client_id self.account_id = self.auth.account_id def setup_urls(self): """Create urls for api.""" try: self.urls = util.BlinkURLHandler(self.auth.region_id) except TypeError: _LOGGER.error( "Unable to extract region is from response %s", self.auth.login_response ) raise BlinkSetupError def setup_networks(self): """Get network information.""" response = api.request_networks(self) try: self.networks = response["summary"] except (KeyError, TypeError): raise BlinkSetupError def setup_network_ids(self): """Create the network ids for onboarded networks.""" all_networks = [] network_dict = {} try: for network, status in self.networks.items(): if status["onboarded"]: all_networks.append(f"{network}") network_dict[status["name"]] = network except AttributeError: _LOGGER.error( "Unable to retrieve network information from %s", self.networks ) raise BlinkSetupError self.network_ids = all_networks return network_dict def check_if_ok_to_update(self): """Check if it is ok to perform an http request.""" current_time = int(time.time()) last_refresh = self.last_refresh if last_refresh is None: last_refresh = 0 if current_time >= (last_refresh + self.refresh_rate): return True return False def merge_cameras(self): """Merge all sync camera dicts into one.""" combined = CaseInsensitiveDict({}) for sync in self.sync: combined = util.merge_dicts(combined, self.sync[sync].cameras) return combined def save(self, file_name): """Save login data to file.""" util.json_save(self.auth.login_attributes, file_name) def download_videos( self, path, since=None, camera="all", stop=10, delay=1, debug=False ): """ Download all videos from server since specified time. :param path: Path to write files. /path/<cameraname>_<recorddate>.mp4 :param since: Date and time to get videos from. Ex: "2018/07/28 12:33:00" to retrieve videos since July 28th 2018 at 12:33:00 :param camera: Camera name to retrieve. Defaults to "all". Use a list for multiple cameras. :param stop: Page to stop on (~25 items per page. Default page 10). :param delay: Number of seconds to wait in between subsequent video downloads. :param debug: Set to TRUE to prevent downloading of items. Instead of downloading, entries will be printed to log. """ if since is None: since_epochs = self.last_refresh else: parsed_datetime = parse(since, fuzzy=True) since_epochs = parsed_datetime.timestamp() formatted_date = util.get_time(time_to_convert=since_epochs) _LOGGER.info("Retrieving videos since %s", formatted_date) if not isinstance(camera, list): camera = [camera] for page in range(1, stop): response = api.request_videos(self, time=since_epochs, page=page) _LOGGER.debug("Processing page %s", page) try: result = response["media"] if not result: raise KeyError except (KeyError, TypeError): _LOGGER.info("No videos found on page %s. Exiting.", page) break self._parse_downloaded_items(result, camera, path, delay, debug) def _parse_downloaded_items(self, result, camera, path, delay, debug): """Parse downloaded videos.""" for item in result: try: created_at = item["created_at"] camera_name = item["device_name"] is_deleted = item["deleted"] address = item["media"] except KeyError: _LOGGER.info("Missing clip information, skipping...") continue if camera_name not in camera and "all" not in camera: _LOGGER.debug("Skipping videos for %s.", camera_name) continue if is_deleted: _LOGGER.debug("%s: %s is marked as deleted.", camera_name, address) continue clip_address = f"{self.urls.base_url}{address}" filename = f"{camera_name}-{created_at}" filename = f"{slugify(filename)}.mp4" filename = os.path.join(path, filename) if not debug: if os.path.isfile(filename): _LOGGER.info("%s already exists, skipping...", filename) continue response = api.http_get( self, url=clip_address, stream=True, json=False, timeout=TIMEOUT_MEDIA, ) with open(filename, "wb") as vidfile: copyfileobj(response.raw, vidfile) _LOGGER.info("Downloaded video to %s", filename) else: print( ( f"Camera: {camera_name}, Timestamp: {created_at}, " "Address: {address}, Filename: {filename}" ) ) if delay > 0: time.sleep(delay)
file_path = f'/uploads/{year}/{month}' return file_path FILENAME= 'blink_creds.json' #FILENAME = re.sub(r"\/.*\/(.*\.\w{1,4}",r'\1',FILE) #BLOB_UPLOAD = BLINK_BUCKET.blob(f"{create_file_path()[1:]}/{FILENAME}") #Set filename format (uploads/year/month/filename). #BLOB_UPLOAD.upload_from_filename(FILE) USER_NAME, PASSWORD = load_credentials() AUTH = Auth({"username": USER_NAME, "password": PASSWORD}, no_prompt=True) blink = Blink() blink.auth = AUTH blink.start() AUTH.send_auth_key(blink, '399587') blink.setup_post_verify() print(type(blink.save(f'{FILENAME}'))) CREDS = json_load("blink_creds.json") blob_blink = BLINK_BUCKET.blob('blink_creds.json') blob_blink.upload_from_string( data=json.dumps(CREDS), content_type='application/json' )