diff --git a/homeassistant/components/rss_feed_template.py b/homeassistant/components/rss_feed_template.py new file mode 100644 index 00000000000..a94ab2d04af --- /dev/null +++ b/homeassistant/components/rss_feed_template.py @@ -0,0 +1,104 @@ +""" +Exports sensor values via RSS feed. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/rss_feed_template/ +""" + +import asyncio +from html import escape +from aiohttp import web + +import voluptuous as vol + +from homeassistant.components.http import HomeAssistantView +import homeassistant.helpers.config_validation as cv + +DOMAIN = "rss_feed_template" +DEPENDENCIES = ['http'] + +CONTENT_TYPE_XML = "text/xml" + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: cv.ordered_dict( + vol.Schema({ + vol.Optional('requires_api_password', default=True): cv.boolean, + vol.Optional('title'): cv.template, + vol.Required('items'): vol.All( + cv.ensure_list, + [{ + vol.Optional('title'): cv.template, + vol.Optional('description'): cv.template, + }] + ) + }) + ) + }, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Setup the RSS feeds.""" + for (feeduri, feedconfig) in config[DOMAIN].items(): + url = '/api/rss_template/%s' % feeduri + + requires_auth = feedconfig.get('requires_api_password') + + title = feedconfig.get('title') + if title is not None: + title.hass = hass + + items = feedconfig.get('items') + for item in items: + if 'title' in item: + item['title'].hass = hass + if 'description' in item: + item['description'].hass = hass + + rss_view = RssView(url, requires_auth, title, items) + hass.http.register_view(rss_view) + + return True + + +class RssView(HomeAssistantView): + """Export states and other values as RSS.""" + + requires_auth = True + url = None + name = 'rss_template' + _title = None + _items = None + + def __init__(self, url, requires_auth, title, items): + """Initialize the rss view.""" + self.url = url + self.requires_auth = requires_auth + self._title = title + self._items = items + + @asyncio.coroutine + def get(self, request, entity_id=None): + """Generate the rss view XML.""" + response = '\n\n' + + response += '\n' + if self._title is not None: + response += (' %s\n' % + escape(self._title.async_render())) + + for item in self._items: + response += ' \n' + if 'title' in item: + response += ' ' + response += escape(item['title'].async_render()) + response += '\n' + if 'description' in item: + response += ' ' + response += escape(item['description'].async_render()) + response += '\n' + response += ' \n' + + response += '\n' + + return web.Response( + body=response, content_type=CONTENT_TYPE_XML, status=200) diff --git a/tests/components/test_rss_feed_template.py b/tests/components/test_rss_feed_template.py new file mode 100644 index 00000000000..60eb2530ea1 --- /dev/null +++ b/tests/components/test_rss_feed_template.py @@ -0,0 +1,49 @@ +"""The tests for the rss_feed_api component.""" +import asyncio +from xml.etree import ElementTree + +import pytest + +from homeassistant.setup import async_setup_component + + +@pytest.fixture +def mock_http_client(loop, hass, test_client): + """Setup test fixture.""" + config = { + 'rss_feed_template': { + 'testfeed': { + 'title': 'feed title is {{states.test.test1.state}}', + 'items': [{ + 'title': 'item title is {{states.test.test2.state}}', + 'description': 'desc {{states.test.test3.state}}'}]}}} + + loop.run_until_complete(async_setup_component(hass, + 'rss_feed_template', + config)) + return loop.run_until_complete(test_client(hass.http.app)) + + +@asyncio.coroutine +def test_get_noexistant_feed(mock_http_client): + """Test if we can retrieve the correct rss feed.""" + resp = yield from mock_http_client.get('/api/rss_template/otherfeed') + assert resp.status == 404 + + +@asyncio.coroutine +def test_get_rss_feed(mock_http_client, hass): + """Test if we can retrieve the correct rss feed.""" + hass.states.async_set('test.test1', 'a_state_1') + hass.states.async_set('test.test2', 'a_state_2') + hass.states.async_set('test.test3', 'a_state_3') + + resp = yield from mock_http_client.get('/api/rss_template/testfeed') + assert resp.status == 200 + + text = yield from resp.text() + + xml = ElementTree.fromstring(text) + assert xml[0].text == 'feed title is a_state_1' + assert xml[1][0].text == 'item title is a_state_2' + assert xml[1][1].text == 'desc a_state_3'