mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Feature/rss feed template (#7032)
* rss_feed_template initial checking * lint * Remove use of deprecated cgi-escape() * Switching back to chardet==2.3 (resolve failing tests with 3.0) * Code and test improvments * Option 'requires_api_password', default is True
This commit is contained in:
parent
f65d8e1254
commit
699cc7213d
104
homeassistant/components/rss_feed_template.py
Normal file
104
homeassistant/components/rss_feed_template.py
Normal file
@ -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 = '<?xml version="1.0" encoding="utf-8"?>\n\n'
|
||||
|
||||
response += '<rss>\n'
|
||||
if self._title is not None:
|
||||
response += (' <title>%s</title>\n' %
|
||||
escape(self._title.async_render()))
|
||||
|
||||
for item in self._items:
|
||||
response += ' <item>\n'
|
||||
if 'title' in item:
|
||||
response += ' <title>'
|
||||
response += escape(item['title'].async_render())
|
||||
response += '</title>\n'
|
||||
if 'description' in item:
|
||||
response += ' <description>'
|
||||
response += escape(item['description'].async_render())
|
||||
response += '</description>\n'
|
||||
response += ' </item>\n'
|
||||
|
||||
response += '</rss>\n'
|
||||
|
||||
return web.Response(
|
||||
body=response, content_type=CONTENT_TYPE_XML, status=200)
|
49
tests/components/test_rss_feed_template.py
Normal file
49
tests/components/test_rss_feed_template.py
Normal file
@ -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'
|
Loading…
x
Reference in New Issue
Block a user