diff --git a/CHANGES.rst b/CHANGES.rst index 2e592afe..ba8ba660 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,18 @@ Changelog A list of changes between each release +0.12.1 (2019-01-31) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Remove logging improvements since they were incompatible with home-assistant logging + +0.12.0 (2019-01-31) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Fix video api endpoint, re-enables motion detection +- Add improved logging capability +- Add download video method +- Prevent blinkpy from failing at setup due to api error + + 0.11.2 (2019-01-23) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Hotfix to prevent platform from stalling due to API change diff --git a/README.rst b/README.rst index 47465a4f..cbfc1e7c 100644 --- a/README.rst +++ b/README.rst @@ -51,23 +51,6 @@ The simplest way to use this package from a terminal is to call ``Blink.start()` If you would like to log in without setting up the cameras or system, you can simply call the ``Blink.login()`` function which will prompt for a username and password and then authenticate with the server. This is useful if you want to avoid use of the ``start()`` function which simply acts as a wrapper for more targeted API methods. -At initialization, you may also set the logging level of the ``blinkpy`` library like so (default is ``INFO``: - -.. code:: python - - import logging - from blinkpy import blinkpy - blink = blinkpy.Blink(..., loglevel=logging.) - blink.start() - -You can also disable logging of duplicate entries via the ``allow_duplicate_logs`` flag (default is ``True``): - -.. code:: python - - from blinkpy import blinkpy - blink = blinkpy.Blink(..., allow_duplicate_logs=False) - blink.start() - Cameras are instantiated as individual ``BlinkCamera`` classes within a ``BlinkSyncModule`` instance. All of your sync modules are stored within the ``Blink.sync`` dictionary and can be accessed using the name of the sync module as the key (this is the name of your sync module in the Blink App). The below code will display cameras and their available attributes: diff --git a/blinkpy/blinkpy.py b/blinkpy/blinkpy.py index 195d8098..9c0d48af 100644 --- a/blinkpy/blinkpy.py +++ b/blinkpy/blinkpy.py @@ -25,7 +25,6 @@ from blinkpy import api from blinkpy.sync_module import BlinkSyncModule from blinkpy.helpers import errors as ERROR -from blinkpy.helpers import log from blinkpy.helpers.util import ( create_session, merge_dicts, get_time, BlinkURLHandler, BlinkAuthenticationException) @@ -35,15 +34,14 @@ REFRESH_RATE = 30 -_LOGGER = log.create_logger('blinkpy') +_LOGGER = logging.getLogger(__name__) class Blink(): """Class to initialize communication.""" def __init__(self, username=None, password=None, - refresh_rate=REFRESH_RATE, loglevel=logging.INFO, - allow_duplicate_logs=True): + refresh_rate=REFRESH_RATE): """ Initialize Blink system. @@ -51,9 +49,6 @@ def __init__(self, username=None, password=None, :param password: Blink password :param refresh_rate: Refresh rate of blink information. Defaults to 15 (seconds) - :param loglevel: Sets the log level for the logger. - :param allow_duplicate_logs: Set to 'False' to only allow a log - message to be logged once. """ self._username = username self._password = password @@ -74,10 +69,6 @@ def __init__(self, username=None, password=None, self.video_list = CaseInsensitiveDict({}) self._login_url = LOGIN_URL self.version = __version__ - self.allow_duplicate_logs = allow_duplicate_logs - - self.loglevel = loglevel - self._reset_logger() @property def auth_header(self): @@ -91,8 +82,6 @@ def start(self): Method logs in and sets auth token, urls, and ids for future requests. Essentially this is just a wrapper function for ease of use. """ - self._reset_logger() - if self._username is None or self._password is None: if not self.login(): return @@ -111,7 +100,7 @@ def login(self): self._username = input("Username:") self._password = getpass.getpass("Password:") if self.get_auth_token(): - _LOGGER.info("Login successful!") + _LOGGER.debug("Login successful!") return True _LOGGER.warning("Unable to login with %s.", self._username) return False @@ -285,19 +274,3 @@ def _parse_downloaded_items(self, result, camera, path): copyfileobj(response.raw, vidfile) _LOGGER.info("Downloaded video to %s", filename) - - # pylint: disable=no-self-use - def _reset_logger(self): - """Reset the log handler.""" - for handler in _LOGGER.handlers: - _LOGGER.removeHandler(handler) - handler.close() - _LOGGER.setLevel(self.loglevel) - if self.allow_duplicate_logs: - handler = logging.StreamHandler() - handler.setFormatter(log.log_formatter()) - else: - handler = log.RepeatLogHandler() - handler.setFormatter(log.log_formatter()) - - _LOGGER.addHandler(handler) diff --git a/blinkpy/helpers/constants.py b/blinkpy/helpers/constants.py index e3f94a44..b647740f 100644 --- a/blinkpy/helpers/constants.py +++ b/blinkpy/helpers/constants.py @@ -4,7 +4,7 @@ MAJOR_VERSION = 0 MINOR_VERSION = 12 -PATCH_VERSION = 0 +PATCH_VERSION = 1 __version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) diff --git a/blinkpy/helpers/log.py b/blinkpy/helpers/log.py deleted file mode 100644 index c13f02b0..00000000 --- a/blinkpy/helpers/log.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Module for blinkpy logging.""" -import logging - - -def create_logger(name): - """Create a logger instance.""" - handler = RepeatLogHandler() - handler.setFormatter(log_formatter()) - logger = logging.getLogger(name) - logger.setLevel(logging.DEBUG) - logger.addHandler(handler) - return logger - - -def log_formatter(): - """Create log formatter.""" - fmt = "%(asctime)s %(levelname)s [%(name)s] %(message)s" - return logging.Formatter(fmt) - - -class RepeatLogHandler(logging.StreamHandler): - """Log handler for repeat entries.""" - - def __init__(self): - """Initialize repeat log handler.""" - super().__init__() - self.log_record = set() - - def emit(self, record): - """Ensure we only log a message once.""" - if record.msg not in self.log_record: - self.log_record.add(record.msg) - super().emit(record) diff --git a/tests/test_blink_functions.py b/tests/test_blink_functions.py index f74f4c12..011c0de1 100644 --- a/tests/test_blink_functions.py +++ b/tests/test_blink_functions.py @@ -90,14 +90,17 @@ def test_merge_cameras(self, mock_sess): @mock.patch('blinkpy.blinkpy.api.request_videos') def test_download_video_exit(self, mock_req, mock_sess): """Test we exit method when provided bad response.""" - blink = blinkpy.Blink(loglevel=logging.DEBUG) + blink = blinkpy.Blink() + # pylint: disable=protected-access + blinkpy._LOGGER.setLevel(logging.DEBUG) blink.last_refresh = 0 mock_req.return_value = {} formatted_date = get_time(blink.last_refresh) expected_log = [ - "INFO:blinkpy:Retrieving videos since {}".format(formatted_date), - "DEBUG:blinkpy:Processing page 1", - "INFO:blinkpy:No videos found on page 1. Exiting." + "INFO:blinkpy.blinkpy:Retrieving videos since {}".format( + formatted_date), + "DEBUG:blinkpy.blinkpy:Processing page 1", + "INFO:blinkpy.blinkpy:No videos found on page 1. Exiting." ] with self.assertLogs() as dl_log: blink.download_videos('/tmp') @@ -106,7 +109,9 @@ def test_download_video_exit(self, mock_req, mock_sess): @mock.patch('blinkpy.blinkpy.api.request_videos') def test_parse_downloaded_items(self, mock_req, mock_sess): """Test ability to parse downloaded items list.""" - blink = blinkpy.Blink(loglevel=logging.DEBUG) + blink = blinkpy.Blink() + # pylint: disable=protected-access + blinkpy._LOGGER.setLevel(logging.DEBUG) generic_entry = { 'created_at': '1970', 'camera_name': 'foo', @@ -118,9 +123,10 @@ def test_parse_downloaded_items(self, mock_req, mock_sess): blink.last_refresh = 0 formatted_date = get_time(blink.last_refresh) expected_log = [ - "INFO:blinkpy:Retrieving videos since {}".format(formatted_date), - "DEBUG:blinkpy:Processing page 1", - "DEBUG:blinkpy:foo: /bar.mp4 is marked as deleted." + "INFO:blinkpy.blinkpy:Retrieving videos since {}".format( + formatted_date), + "DEBUG:blinkpy.blinkpy:Processing page 1", + "DEBUG:blinkpy.blinkpy:foo: /bar.mp4 is marked as deleted." ] with self.assertLogs() as dl_log: blink.download_videos('/tmp', stop=2) @@ -129,7 +135,9 @@ def test_parse_downloaded_items(self, mock_req, mock_sess): @mock.patch('blinkpy.blinkpy.api.request_videos') def test_parse_camera_not_in_list(self, mock_req, mock_sess): """Test ability to parse downloaded items list.""" - blink = blinkpy.Blink(loglevel=logging.DEBUG) + blink = blinkpy.Blink() + # pylint: disable=protected-access + blinkpy._LOGGER.setLevel(logging.DEBUG) generic_entry = { 'created_at': '1970', 'camera_name': 'foo', @@ -141,9 +149,10 @@ def test_parse_camera_not_in_list(self, mock_req, mock_sess): blink.last_refresh = 0 formatted_date = get_time(blink.last_refresh) expected_log = [ - "INFO:blinkpy:Retrieving videos since {}".format(formatted_date), - "DEBUG:blinkpy:Processing page 1", - "DEBUG:blinkpy:Skipping videos for foo." + "INFO:blinkpy.blinkpy:Retrieving videos since {}".format( + formatted_date), + "DEBUG:blinkpy.blinkpy:Processing page 1", + "DEBUG:blinkpy.blinkpy:Skipping videos for foo." ] with self.assertLogs() as dl_log: blink.download_videos('/tmp', camera='bar', stop=2)