diff --git a/homeassistant/components/feedreader.py b/homeassistant/components/feedreader.py index 61fbe9f3171..73ab9e8123c 100644 --- a/homeassistant/components/feedreader.py +++ b/homeassistant/components/feedreader.py @@ -4,7 +4,7 @@ Support for RSS/Atom feeds. For more details about this component, please refer to the documentation at https://home-assistant.io/components/feedreader/ """ -from datetime import datetime +from datetime import datetime, timedelta from logging import getLogger from os.path import exists from threading import Lock @@ -12,8 +12,8 @@ import pickle import voluptuous as vol -from homeassistant.const import EVENT_HOMEASSISTANT_START -from homeassistant.helpers.event import track_utc_time_change +from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL +from homeassistant.helpers.event import track_time_interval import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['feedparser==5.2.1'] @@ -21,16 +21,22 @@ REQUIREMENTS = ['feedparser==5.2.1'] _LOGGER = getLogger(__name__) CONF_URLS = 'urls' +CONF_MAX_ENTRIES = 'max_entries' + +DEFAULT_MAX_ENTRIES = 20 +DEFAULT_SCAN_INTERVAL = timedelta(hours=1) DOMAIN = 'feedreader' EVENT_FEEDREADER = 'feedreader' -MAX_ENTRIES = 20 - CONFIG_SCHEMA = vol.Schema({ DOMAIN: { vol.Required(CONF_URLS): vol.All(cv.ensure_list, [cv.url]), + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): + cv.time_period, + vol.Optional(CONF_MAX_ENTRIES, default=DEFAULT_MAX_ENTRIES): + cv.positive_int } }, extra=vol.ALLOW_EXTRA) @@ -38,18 +44,23 @@ CONFIG_SCHEMA = vol.Schema({ def setup(hass, config): """Set up the Feedreader component.""" urls = config.get(DOMAIN)[CONF_URLS] + scan_interval = config.get(DOMAIN).get(CONF_SCAN_INTERVAL) + max_entries = config.get(DOMAIN).get(CONF_MAX_ENTRIES) data_file = hass.config.path("{}.pickle".format(DOMAIN)) storage = StoredData(data_file) - feeds = [FeedManager(url, hass, storage) for url in urls] + feeds = [FeedManager(url, scan_interval, max_entries, hass, storage) for + url in urls] return len(feeds) > 0 class FeedManager(object): """Abstraction over Feedparser module.""" - def __init__(self, url, hass, storage): - """Initialize the FeedManager object, poll every hour.""" + def __init__(self, url, scan_interval, max_entries, hass, storage): + """Initialize the FeedManager object, poll as per scan interval.""" self._url = url + self._scan_interval = scan_interval + self._max_entries = max_entries self._feed = None self._hass = hass self._firstrun = True @@ -69,8 +80,8 @@ class FeedManager(object): def _init_regular_updates(self, hass): """Schedule regular updates at the top of the clock.""" - track_utc_time_change( - hass, lambda now: self._update(), minute=0, second=0) + track_time_interval(hass, lambda now: self._update(), + self._scan_interval) @property def last_update_successful(self): @@ -116,10 +127,10 @@ class FeedManager(object): def _filter_entries(self): """Filter the entries provided and return the ones to keep.""" - if len(self._feed.entries) > MAX_ENTRIES: + if len(self._feed.entries) > self._max_entries: _LOGGER.debug("Processing only the first %s entries " - "in feed %s", MAX_ENTRIES, self._url) - self._feed.entries = self._feed.entries[0:MAX_ENTRIES] + "in feed %s", self._max_entries, self._url) + self._feed.entries = self._feed.entries[0:self._max_entries] def _update_and_fire_entry(self, entry): """Update last_entry_timestamp and fire entry.""" diff --git a/tests/components/test_feedreader.py b/tests/components/test_feedreader.py index 2288e21e37a..c20b297017c 100644 --- a/tests/components/test_feedreader.py +++ b/tests/components/test_feedreader.py @@ -1,6 +1,6 @@ """The tests for the feedreader component.""" import time -from datetime import datetime +from datetime import datetime, timedelta import unittest from genericpath import exists @@ -11,12 +11,12 @@ from unittest.mock import patch from homeassistant.components import feedreader from homeassistant.components.feedreader import CONF_URLS, FeedManager, \ - StoredData, EVENT_FEEDREADER -from homeassistant.const import EVENT_HOMEASSISTANT_START + StoredData, EVENT_FEEDREADER, DEFAULT_SCAN_INTERVAL, CONF_MAX_ENTRIES, \ + DEFAULT_MAX_ENTRIES +from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL from homeassistant.core import callback from homeassistant.setup import setup_component -from tests.common import get_test_home_assistant, assert_setup_component, \ - load_fixture +from tests.common import get_test_home_assistant, load_fixture _LOGGER = getLogger(__name__) @@ -26,6 +26,18 @@ VALID_CONFIG_1 = { CONF_URLS: [URL] } } +VALID_CONFIG_2 = { + feedreader.DOMAIN: { + CONF_URLS: [URL], + CONF_SCAN_INTERVAL: 60 + } +} +VALID_CONFIG_3 = { + feedreader.DOMAIN: { + CONF_URLS: [URL], + CONF_MAX_ENTRIES: 100 + } +} class TestFeedreaderComponent(unittest.TestCase): @@ -45,11 +57,28 @@ class TestFeedreaderComponent(unittest.TestCase): def test_setup_one_feed(self): """Test the general setup of this component.""" - with assert_setup_component(1, 'feedreader'): + with patch("homeassistant.components.feedreader." + "track_time_interval") as track_method: self.assertTrue(setup_component(self.hass, feedreader.DOMAIN, VALID_CONFIG_1)) + track_method.assert_called_once_with(self.hass, mock.ANY, + DEFAULT_SCAN_INTERVAL) - def setup_manager(self, feed_data): + def test_setup_scan_interval(self): + """Test the setup of this component with scan interval.""" + with patch("homeassistant.components.feedreader." + "track_time_interval") as track_method: + self.assertTrue(setup_component(self.hass, feedreader.DOMAIN, + VALID_CONFIG_2)) + track_method.assert_called_once_with(self.hass, mock.ANY, + timedelta(seconds=60)) + + def test_setup_max_entries(self): + """Test the setup of this component with max entries.""" + self.assertTrue(setup_component(self.hass, feedreader.DOMAIN, + VALID_CONFIG_3)) + + def setup_manager(self, feed_data, max_entries=DEFAULT_MAX_ENTRIES): """Generic test setup method.""" events = [] @@ -67,12 +96,13 @@ class TestFeedreaderComponent(unittest.TestCase): feedreader.DOMAIN)) storage = StoredData(data_file) with patch("homeassistant.components.feedreader." - "track_utc_time_change") as track_method: - manager = FeedManager(feed_data, self.hass, storage) + "track_time_interval") as track_method: + manager = FeedManager(feed_data, DEFAULT_SCAN_INTERVAL, + max_entries, self.hass, storage) # Can't use 'assert_called_once' here because it's not available # in Python 3.5 yet. - track_method.assert_called_once_with(self.hass, mock.ANY, minute=0, - second=0) + track_method.assert_called_once_with(self.hass, mock.ANY, + DEFAULT_SCAN_INTERVAL) # Artificially trigger update. self.hass.bus.fire(EVENT_HOMEASSISTANT_START) # Collect events. @@ -116,12 +146,18 @@ class TestFeedreaderComponent(unittest.TestCase): manager3, events3 = self.setup_manager(feed_data3) assert len(events3) == 0 - def test_feed_max_length(self): - """Test long feed beyond the 20 entry limit.""" + def test_feed_default_max_length(self): + """Test long feed beyond the default 20 entry limit.""" feed_data = load_fixture('feedreader2.xml') manager, events = self.setup_manager(feed_data) assert len(events) == 20 + def test_feed_max_length(self): + """Test long feed beyond a configured 5 entry limit.""" + feed_data = load_fixture('feedreader2.xml') + manager, events = self.setup_manager(feed_data, max_entries=5) + assert len(events) == 5 + def test_feed_without_publication_date(self): """Test simple feed with entry without publication date.""" feed_data = load_fixture('feedreader3.xml') @@ -141,7 +177,8 @@ class TestFeedreaderComponent(unittest.TestCase): data_file = self.hass.config.path("{}.pickle".format( feedreader.DOMAIN)) storage = StoredData(data_file) - manager = FeedManager("FEED DATA", self.hass, storage) + manager = FeedManager("FEED DATA", DEFAULT_SCAN_INTERVAL, + DEFAULT_MAX_ENTRIES, self.hass, storage) # Artificially trigger update. self.hass.bus.fire(EVENT_HOMEASSISTANT_START) # Collect events.