mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
GeoRSS events sensor refactored (#16939)
* refactored geo_rss_events sensor to make use of new georss-client library that handles the communication with the rss feed * fixed lint error
This commit is contained in:
parent
b0b3620b2b
commit
13af61e103
@ -9,7 +9,6 @@ For more details about this platform, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/sensor.geo_rss_events/
|
https://home-assistant.io/components/sensor.geo_rss_events/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -19,9 +18,8 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_UNKNOWN, CONF_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_RADIUS, CONF_URL)
|
STATE_UNKNOWN, CONF_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_RADIUS, CONF_URL)
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
|
||||||
|
|
||||||
REQUIREMENTS = ['feedparser==5.2.1', 'haversine==0.4.5']
|
REQUIREMENTS = ['georss_client==0.1']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -38,9 +36,6 @@ DEFAULT_UNIT_OF_MEASUREMENT = 'Events'
|
|||||||
|
|
||||||
DOMAIN = 'geo_rss_events'
|
DOMAIN = 'geo_rss_events'
|
||||||
|
|
||||||
# Minimum time between updates from the source.
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
|
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(minutes=5)
|
SCAN_INTERVAL = timedelta(minutes=5)
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
@ -67,18 +62,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
_LOGGER.debug("latitude=%s, longitude=%s, url=%s, radius=%s",
|
_LOGGER.debug("latitude=%s, longitude=%s, url=%s, radius=%s",
|
||||||
home_latitude, home_longitude, url, radius_in_km)
|
home_latitude, home_longitude, url, radius_in_km)
|
||||||
|
|
||||||
# Initialise update service.
|
|
||||||
data = GeoRssServiceData(home_latitude, home_longitude, url, radius_in_km)
|
|
||||||
data.update()
|
|
||||||
|
|
||||||
# Create all sensors based on categories.
|
# Create all sensors based on categories.
|
||||||
devices = []
|
devices = []
|
||||||
if not categories:
|
if not categories:
|
||||||
device = GeoRssServiceSensor(None, data, name, unit_of_measurement)
|
device = GeoRssServiceSensor((home_latitude, home_longitude), url,
|
||||||
|
radius_in_km, None, name,
|
||||||
|
unit_of_measurement)
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
else:
|
else:
|
||||||
for category in categories:
|
for category in categories:
|
||||||
device = GeoRssServiceSensor(category, data, name,
|
device = GeoRssServiceSensor((home_latitude, home_longitude), url,
|
||||||
|
radius_in_km, category, name,
|
||||||
unit_of_measurement)
|
unit_of_measurement)
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
add_entities(devices, True)
|
add_entities(devices, True)
|
||||||
@ -87,14 +81,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
class GeoRssServiceSensor(Entity):
|
class GeoRssServiceSensor(Entity):
|
||||||
"""Representation of a Sensor."""
|
"""Representation of a Sensor."""
|
||||||
|
|
||||||
def __init__(self, category, data, service_name, unit_of_measurement):
|
def __init__(self, home_coordinates, url, radius, category, service_name,
|
||||||
|
unit_of_measurement):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self._category = category
|
self._category = category
|
||||||
self._data = data
|
|
||||||
self._service_name = service_name
|
self._service_name = service_name
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
self._state_attributes = None
|
self._state_attributes = None
|
||||||
self._unit_of_measurement = unit_of_measurement
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
from georss_client.generic_feed import GenericFeed
|
||||||
|
self._feed = GenericFeed(home_coordinates, url, filter_radius=radius,
|
||||||
|
filter_categories=None if not category
|
||||||
|
else [category])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -125,115 +123,25 @@ class GeoRssServiceSensor(Entity):
|
|||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update this sensor from the GeoRSS service."""
|
"""Update this sensor from the GeoRSS service."""
|
||||||
_LOGGER.debug("About to update sensor %s", self.entity_id)
|
import georss_client
|
||||||
self._data.update()
|
status, feed_entries = self._feed.update()
|
||||||
# If no events were found due to an error then just set state to zero.
|
if status == georss_client.UPDATE_OK:
|
||||||
if self._data.events is None:
|
|
||||||
self._state = 0
|
|
||||||
else:
|
|
||||||
if self._category is None:
|
|
||||||
# Add all events regardless of category.
|
|
||||||
my_events = self._data.events
|
|
||||||
else:
|
|
||||||
# Only keep events that belong to sensor's category.
|
|
||||||
my_events = [event for event in self._data.events if
|
|
||||||
event[ATTR_CATEGORY] == self._category]
|
|
||||||
_LOGGER.debug("Adding events to sensor %s: %s", self.entity_id,
|
_LOGGER.debug("Adding events to sensor %s: %s", self.entity_id,
|
||||||
my_events)
|
feed_entries)
|
||||||
self._state = len(my_events)
|
self._state = len(feed_entries)
|
||||||
# And now compute the attributes from the filtered events.
|
# And now compute the attributes from the filtered events.
|
||||||
matrix = {}
|
matrix = {}
|
||||||
for event in my_events:
|
for entry in feed_entries:
|
||||||
matrix[event[ATTR_TITLE]] = '{:.0f}km'.format(
|
matrix[entry.title] = '{:.0f}km'.format(
|
||||||
event[ATTR_DISTANCE])
|
entry.distance_to_home)
|
||||||
self._state_attributes = matrix
|
self._state_attributes = matrix
|
||||||
|
elif status == georss_client.UPDATE_OK_NO_DATA:
|
||||||
|
_LOGGER.debug("Update successful, but no data received from %s",
|
||||||
class GeoRssServiceData:
|
self._feed)
|
||||||
"""Provide access to GeoRSS feed and stores the latest data."""
|
# Don't change the state or state attributes.
|
||||||
|
|
||||||
def __init__(self, home_latitude, home_longitude, url, radius_in_km):
|
|
||||||
"""Initialize the update service."""
|
|
||||||
self._home_coordinates = [home_latitude, home_longitude]
|
|
||||||
self._url = url
|
|
||||||
self._radius_in_km = radius_in_km
|
|
||||||
self.events = None
|
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
|
||||||
def update(self):
|
|
||||||
"""Retrieve data from GeoRSS feed and store events."""
|
|
||||||
import feedparser
|
|
||||||
feed_data = feedparser.parse(self._url)
|
|
||||||
if not feed_data:
|
|
||||||
_LOGGER.error("Error fetching feed data from %s", self._url)
|
|
||||||
else:
|
else:
|
||||||
events = self.filter_entries(feed_data)
|
_LOGGER.warning("Update not successful, no data received from %s",
|
||||||
self.events = events
|
self._feed)
|
||||||
|
# If no events were found due to an error then just set state to
|
||||||
def filter_entries(self, feed_data):
|
# zero.
|
||||||
"""Filter entries by distance from home coordinates."""
|
self._state = 0
|
||||||
events = []
|
|
||||||
_LOGGER.debug("%s entri(es) available in feed %s",
|
|
||||||
len(feed_data.entries), self._url)
|
|
||||||
for entry in feed_data.entries:
|
|
||||||
geometry = None
|
|
||||||
if hasattr(entry, 'where'):
|
|
||||||
geometry = entry.where
|
|
||||||
elif hasattr(entry, 'geo_lat') and hasattr(entry, 'geo_long'):
|
|
||||||
coordinates = (float(entry.geo_long), float(entry.geo_lat))
|
|
||||||
point = namedtuple('Point', ['type', 'coordinates'])
|
|
||||||
geometry = point('Point', coordinates)
|
|
||||||
if geometry:
|
|
||||||
distance = self.calculate_distance_to_geometry(geometry)
|
|
||||||
if distance <= self._radius_in_km:
|
|
||||||
event = {
|
|
||||||
ATTR_CATEGORY: None if not hasattr(
|
|
||||||
entry, 'category') else entry.category,
|
|
||||||
ATTR_TITLE: None if not hasattr(
|
|
||||||
entry, 'title') else entry.title,
|
|
||||||
ATTR_DISTANCE: distance
|
|
||||||
}
|
|
||||||
events.append(event)
|
|
||||||
_LOGGER.debug("%s events found nearby", len(events))
|
|
||||||
return events
|
|
||||||
|
|
||||||
def calculate_distance_to_geometry(self, geometry):
|
|
||||||
"""Calculate the distance between HA and provided geometry."""
|
|
||||||
distance = float("inf")
|
|
||||||
if geometry.type == 'Point':
|
|
||||||
distance = self.calculate_distance_to_point(geometry)
|
|
||||||
elif geometry.type == 'Polygon':
|
|
||||||
distance = self.calculate_distance_to_polygon(
|
|
||||||
geometry.coordinates[0])
|
|
||||||
else:
|
|
||||||
_LOGGER.warning("Not yet implemented: %s", geometry.type)
|
|
||||||
return distance
|
|
||||||
|
|
||||||
def calculate_distance_to_point(self, point):
|
|
||||||
"""Calculate the distance between HA and the provided point."""
|
|
||||||
# Swap coordinates to match: (lat, lon).
|
|
||||||
coordinates = (point.coordinates[1], point.coordinates[0])
|
|
||||||
return self.calculate_distance_to_coords(coordinates)
|
|
||||||
|
|
||||||
def calculate_distance_to_coords(self, coordinates):
|
|
||||||
"""Calculate the distance between HA and the provided coordinates."""
|
|
||||||
# Expecting coordinates in format: (lat, lon).
|
|
||||||
from haversine import haversine
|
|
||||||
distance = haversine(coordinates, self._home_coordinates)
|
|
||||||
_LOGGER.debug("Distance from %s to %s: %s km", self._home_coordinates,
|
|
||||||
coordinates, distance)
|
|
||||||
return distance
|
|
||||||
|
|
||||||
def calculate_distance_to_polygon(self, polygon):
|
|
||||||
"""Calculate the distance between HA and the provided polygon."""
|
|
||||||
distance = float("inf")
|
|
||||||
# Calculate distance from polygon by calculating the distance
|
|
||||||
# to each point of the polygon but not to each edge of the
|
|
||||||
# polygon; should be good enough
|
|
||||||
for polygon_point in polygon:
|
|
||||||
coordinates = (polygon_point[1], polygon_point[0])
|
|
||||||
distance = min(distance,
|
|
||||||
self.calculate_distance_to_coords(coordinates))
|
|
||||||
_LOGGER.debug("Distance from %s to %s: %s km", self._home_coordinates,
|
|
||||||
polygon, distance)
|
|
||||||
return distance
|
|
||||||
|
@ -356,7 +356,6 @@ fastdotcom==0.0.3
|
|||||||
fedexdeliverymanager==1.0.6
|
fedexdeliverymanager==1.0.6
|
||||||
|
|
||||||
# homeassistant.components.feedreader
|
# homeassistant.components.feedreader
|
||||||
# homeassistant.components.sensor.geo_rss_events
|
|
||||||
feedparser==5.2.1
|
feedparser==5.2.1
|
||||||
|
|
||||||
# homeassistant.components.sensor.fints
|
# homeassistant.components.sensor.fints
|
||||||
@ -397,6 +396,9 @@ geizhals==0.0.7
|
|||||||
# homeassistant.components.geo_location.geo_json_events
|
# homeassistant.components.geo_location.geo_json_events
|
||||||
geojson_client==0.1
|
geojson_client==0.1
|
||||||
|
|
||||||
|
# homeassistant.components.sensor.geo_rss_events
|
||||||
|
georss_client==0.1
|
||||||
|
|
||||||
# homeassistant.components.sensor.gitter
|
# homeassistant.components.sensor.gitter
|
||||||
gitterpy==0.1.7
|
gitterpy==0.1.7
|
||||||
|
|
||||||
@ -433,9 +435,6 @@ habitipy==0.2.0
|
|||||||
# homeassistant.components.hangouts
|
# homeassistant.components.hangouts
|
||||||
hangups==0.4.5
|
hangups==0.4.5
|
||||||
|
|
||||||
# homeassistant.components.sensor.geo_rss_events
|
|
||||||
haversine==0.4.5
|
|
||||||
|
|
||||||
# homeassistant.components.mqtt.server
|
# homeassistant.components.mqtt.server
|
||||||
hbmqtt==0.9.4
|
hbmqtt==0.9.4
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ ephem==3.7.6.0
|
|||||||
evohomeclient==0.2.7
|
evohomeclient==0.2.7
|
||||||
|
|
||||||
# homeassistant.components.feedreader
|
# homeassistant.components.feedreader
|
||||||
# homeassistant.components.sensor.geo_rss_events
|
|
||||||
feedparser==5.2.1
|
feedparser==5.2.1
|
||||||
|
|
||||||
# homeassistant.components.sensor.foobot
|
# homeassistant.components.sensor.foobot
|
||||||
@ -69,15 +68,15 @@ gTTS-token==1.1.2
|
|||||||
# homeassistant.components.geo_location.geo_json_events
|
# homeassistant.components.geo_location.geo_json_events
|
||||||
geojson_client==0.1
|
geojson_client==0.1
|
||||||
|
|
||||||
|
# homeassistant.components.sensor.geo_rss_events
|
||||||
|
georss_client==0.1
|
||||||
|
|
||||||
# homeassistant.components.ffmpeg
|
# homeassistant.components.ffmpeg
|
||||||
ha-ffmpeg==1.9
|
ha-ffmpeg==1.9
|
||||||
|
|
||||||
# homeassistant.components.hangouts
|
# homeassistant.components.hangouts
|
||||||
hangups==0.4.5
|
hangups==0.4.5
|
||||||
|
|
||||||
# homeassistant.components.sensor.geo_rss_events
|
|
||||||
haversine==0.4.5
|
|
||||||
|
|
||||||
# homeassistant.components.mqtt.server
|
# homeassistant.components.mqtt.server
|
||||||
hbmqtt==0.9.4
|
hbmqtt==0.9.4
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ TEST_REQUIREMENTS = (
|
|||||||
'foobot_async',
|
'foobot_async',
|
||||||
'gTTS-token',
|
'gTTS-token',
|
||||||
'geojson_client',
|
'geojson_client',
|
||||||
|
'georss_client',
|
||||||
'hangups',
|
'hangups',
|
||||||
'HAP-python',
|
'HAP-python',
|
||||||
'ha-ffmpeg',
|
'ha-ffmpeg',
|
||||||
|
@ -2,26 +2,38 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import sys
|
import sys
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import feedparser
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import sensor
|
||||||
|
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME, \
|
||||||
|
EVENT_HOMEASSISTANT_START, ATTR_ICON
|
||||||
from homeassistant.setup import setup_component
|
from homeassistant.setup import setup_component
|
||||||
from tests.common import load_fixture, get_test_home_assistant
|
from tests.common import get_test_home_assistant, \
|
||||||
|
assert_setup_component, fire_time_changed
|
||||||
import homeassistant.components.sensor.geo_rss_events as geo_rss_events
|
import homeassistant.components.sensor.geo_rss_events as geo_rss_events
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
URL = 'http://geo.rss.local/geo_rss_events.xml'
|
URL = 'http://geo.rss.local/geo_rss_events.xml'
|
||||||
VALID_CONFIG_WITH_CATEGORIES = {
|
VALID_CONFIG_WITH_CATEGORIES = {
|
||||||
'platform': 'geo_rss_events',
|
sensor.DOMAIN: [
|
||||||
geo_rss_events.CONF_URL: URL,
|
{
|
||||||
geo_rss_events.CONF_CATEGORIES: [
|
'platform': 'geo_rss_events',
|
||||||
'Category 1',
|
geo_rss_events.CONF_URL: URL,
|
||||||
'Category 2'
|
geo_rss_events.CONF_CATEGORIES: [
|
||||||
|
'Category 1'
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
VALID_CONFIG_WITHOUT_CATEGORIES = {
|
VALID_CONFIG = {
|
||||||
'platform': 'geo_rss_events',
|
sensor.DOMAIN: [
|
||||||
geo_rss_events.CONF_URL: URL
|
{
|
||||||
|
'platform': 'geo_rss_events',
|
||||||
|
geo_rss_events.CONF_URL: URL
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -34,119 +46,114 @@ class TestGeoRssServiceUpdater(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Initialize values for this testcase class."""
|
"""Initialize values for this testcase class."""
|
||||||
self.hass = get_test_home_assistant()
|
self.hass = get_test_home_assistant()
|
||||||
self.config = VALID_CONFIG_WITHOUT_CATEGORIES
|
# self.config = VALID_CONFIG_WITHOUT_CATEGORIES
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop everything that was started."""
|
"""Stop everything that was started."""
|
||||||
self.hass.stop()
|
self.hass.stop()
|
||||||
|
|
||||||
@mock.patch('feedparser.parse', return_value=feedparser.parse(""))
|
@staticmethod
|
||||||
def test_setup_with_categories(self, mock_parse):
|
def _generate_mock_feed_entry(external_id, title, distance_to_home,
|
||||||
"""Test the general setup of this sensor."""
|
coordinates, category):
|
||||||
self.config = VALID_CONFIG_WITH_CATEGORIES
|
"""Construct a mock feed entry for testing purposes."""
|
||||||
self.assertTrue(
|
feed_entry = MagicMock()
|
||||||
setup_component(self.hass, 'sensor', {'sensor': self.config}))
|
feed_entry.external_id = external_id
|
||||||
self.assertIsNotNone(
|
feed_entry.title = title
|
||||||
self.hass.states.get('sensor.event_service_category_1'))
|
feed_entry.distance_to_home = distance_to_home
|
||||||
self.assertIsNotNone(
|
feed_entry.coordinates = coordinates
|
||||||
self.hass.states.get('sensor.event_service_category_2'))
|
feed_entry.category = category
|
||||||
|
return feed_entry
|
||||||
|
|
||||||
@mock.patch('feedparser.parse', return_value=feedparser.parse(""))
|
@mock.patch('georss_client.generic_feed.GenericFeed')
|
||||||
def test_setup_without_categories(self, mock_parse):
|
def test_setup(self, mock_feed):
|
||||||
"""Test the general setup of this sensor."""
|
"""Test the general setup of the platform."""
|
||||||
self.assertTrue(
|
# Set up some mock feed entries for this test.
|
||||||
setup_component(self.hass, 'sensor', {'sensor': self.config}))
|
mock_entry_1 = self._generate_mock_feed_entry('1234', 'Title 1', 15.5,
|
||||||
self.assertIsNotNone(self.hass.states.get('sensor.event_service_any'))
|
(-31.0, 150.0),
|
||||||
|
'Category 1')
|
||||||
|
mock_entry_2 = self._generate_mock_feed_entry('2345', 'Title 2', 20.5,
|
||||||
|
(-31.1, 150.1),
|
||||||
|
'Category 1')
|
||||||
|
mock_feed.return_value.update.return_value = 'OK', [mock_entry_1,
|
||||||
|
mock_entry_2]
|
||||||
|
|
||||||
def setup_data(self, url='url'):
|
utcnow = dt_util.utcnow()
|
||||||
"""Set up data object for use by sensors."""
|
# Patching 'utcnow' to gain more control over the timed update.
|
||||||
home_latitude = -33.865
|
with patch('homeassistant.util.dt.utcnow', return_value=utcnow):
|
||||||
home_longitude = 151.209444
|
with assert_setup_component(1, sensor.DOMAIN):
|
||||||
radius_in_km = 500
|
self.assertTrue(setup_component(self.hass, sensor.DOMAIN,
|
||||||
data = geo_rss_events.GeoRssServiceData(home_latitude,
|
VALID_CONFIG))
|
||||||
home_longitude, url,
|
# Artificially trigger update.
|
||||||
radius_in_km)
|
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||||
return data
|
# Collect events.
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
def test_update_sensor_with_category(self):
|
all_states = self.hass.states.all()
|
||||||
"""Test updating sensor object."""
|
assert len(all_states) == 1
|
||||||
raw_data = load_fixture('geo_rss_events.xml')
|
|
||||||
# Loading raw data from fixture and plug in to data object as URL
|
|
||||||
# works since the third-party feedparser library accepts a URL
|
|
||||||
# as well as the actual data.
|
|
||||||
data = self.setup_data(raw_data)
|
|
||||||
category = "Category 1"
|
|
||||||
name = "Name 1"
|
|
||||||
unit_of_measurement = "Unit 1"
|
|
||||||
sensor = geo_rss_events.GeoRssServiceSensor(category,
|
|
||||||
data, name,
|
|
||||||
unit_of_measurement)
|
|
||||||
|
|
||||||
sensor.update()
|
state = self.hass.states.get("sensor.event_service_any")
|
||||||
assert sensor.name == "Name 1 Category 1"
|
self.assertIsNotNone(state)
|
||||||
assert sensor.unit_of_measurement == "Unit 1"
|
assert state.name == "Event Service Any"
|
||||||
assert sensor.icon == "mdi:alert"
|
assert int(state.state) == 2
|
||||||
assert len(sensor._data.events) == 4
|
assert state.attributes == {
|
||||||
assert sensor.state == 1
|
ATTR_FRIENDLY_NAME: "Event Service Any",
|
||||||
assert sensor.device_state_attributes == {'Title 1': "117km"}
|
ATTR_UNIT_OF_MEASUREMENT: "Events",
|
||||||
# Check entries of first hit
|
ATTR_ICON: "mdi:alert",
|
||||||
assert sensor._data.events[0][geo_rss_events.ATTR_TITLE] == "Title 1"
|
"Title 1": "16km", "Title 2": "20km"}
|
||||||
assert sensor._data.events[0][
|
|
||||||
geo_rss_events.ATTR_CATEGORY] == "Category 1"
|
|
||||||
self.assertAlmostEqual(sensor._data.events[0][
|
|
||||||
geo_rss_events.ATTR_DISTANCE], 116.586, 0)
|
|
||||||
|
|
||||||
def test_update_sensor_without_category(self):
|
# Simulate an update - empty data, but successful update,
|
||||||
"""Test updating sensor object."""
|
# so no changes to entities.
|
||||||
raw_data = load_fixture('geo_rss_events.xml')
|
mock_feed.return_value.update.return_value = 'OK_NO_DATA', None
|
||||||
data = self.setup_data(raw_data)
|
fire_time_changed(self.hass, utcnow +
|
||||||
category = None
|
geo_rss_events.SCAN_INTERVAL)
|
||||||
name = "Name 2"
|
self.hass.block_till_done()
|
||||||
unit_of_measurement = "Unit 2"
|
|
||||||
sensor = geo_rss_events.GeoRssServiceSensor(category,
|
|
||||||
data, name,
|
|
||||||
unit_of_measurement)
|
|
||||||
|
|
||||||
sensor.update()
|
all_states = self.hass.states.all()
|
||||||
assert sensor.name == "Name 2 Any"
|
assert len(all_states) == 1
|
||||||
assert sensor.unit_of_measurement == "Unit 2"
|
state = self.hass.states.get("sensor.event_service_any")
|
||||||
assert sensor.icon == "mdi:alert"
|
assert int(state.state) == 2
|
||||||
assert len(sensor._data.events) == 4
|
|
||||||
assert sensor.state == 4
|
|
||||||
assert sensor.device_state_attributes == {'Title 1': "117km",
|
|
||||||
'Title 2': "302km",
|
|
||||||
'Title 3': "204km",
|
|
||||||
'Title 6': "48km"}
|
|
||||||
|
|
||||||
def test_update_sensor_without_data(self):
|
# Simulate an update - empty data, removes all entities
|
||||||
"""Test updating sensor object."""
|
mock_feed.return_value.update.return_value = 'ERROR', None
|
||||||
data = self.setup_data()
|
fire_time_changed(self.hass, utcnow +
|
||||||
category = None
|
2 * geo_rss_events.SCAN_INTERVAL)
|
||||||
name = "Name 3"
|
self.hass.block_till_done()
|
||||||
unit_of_measurement = "Unit 3"
|
|
||||||
sensor = geo_rss_events.GeoRssServiceSensor(category,
|
|
||||||
data, name,
|
|
||||||
unit_of_measurement)
|
|
||||||
|
|
||||||
sensor.update()
|
all_states = self.hass.states.all()
|
||||||
assert sensor.name == "Name 3 Any"
|
assert len(all_states) == 1
|
||||||
assert sensor.unit_of_measurement == "Unit 3"
|
state = self.hass.states.get("sensor.event_service_any")
|
||||||
assert sensor.icon == "mdi:alert"
|
assert int(state.state) == 0
|
||||||
assert len(sensor._data.events) == 0
|
|
||||||
assert sensor.state == 0
|
|
||||||
|
|
||||||
@mock.patch('feedparser.parse', return_value=None)
|
@mock.patch('georss_client.generic_feed.GenericFeed')
|
||||||
def test_update_sensor_with_none_result(self, parse_function):
|
def test_setup_with_categories(self, mock_feed):
|
||||||
"""Test updating sensor object."""
|
"""Test the general setup of the platform."""
|
||||||
data = self.setup_data("http://invalid.url/")
|
# Set up some mock feed entries for this test.
|
||||||
category = None
|
mock_entry_1 = self._generate_mock_feed_entry('1234', 'Title 1', 15.5,
|
||||||
name = "Name 4"
|
(-31.0, 150.0),
|
||||||
unit_of_measurement = "Unit 4"
|
'Category 1')
|
||||||
sensor = geo_rss_events.GeoRssServiceSensor(category,
|
mock_entry_2 = self._generate_mock_feed_entry('2345', 'Title 2', 20.5,
|
||||||
data, name,
|
(-31.1, 150.1),
|
||||||
unit_of_measurement)
|
'Category 1')
|
||||||
|
mock_feed.return_value.update.return_value = 'OK', [mock_entry_1,
|
||||||
|
mock_entry_2]
|
||||||
|
|
||||||
sensor.update()
|
with assert_setup_component(1, sensor.DOMAIN):
|
||||||
assert sensor.name == "Name 4 Any"
|
self.assertTrue(setup_component(self.hass, sensor.DOMAIN,
|
||||||
assert sensor.unit_of_measurement == "Unit 4"
|
VALID_CONFIG_WITH_CATEGORIES))
|
||||||
assert sensor.state == 0
|
# Artificially trigger update.
|
||||||
|
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
# Collect events.
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
all_states = self.hass.states.all()
|
||||||
|
assert len(all_states) == 1
|
||||||
|
|
||||||
|
state = self.hass.states.get("sensor.event_service_category_1")
|
||||||
|
self.assertIsNotNone(state)
|
||||||
|
assert state.name == "Event Service Category 1"
|
||||||
|
assert int(state.state) == 2
|
||||||
|
assert state.attributes == {
|
||||||
|
ATTR_FRIENDLY_NAME: "Event Service Category 1",
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: "Events",
|
||||||
|
ATTR_ICON: "mdi:alert",
|
||||||
|
"Title 1": "16km", "Title 2": "20km"}
|
||||||
|
76
tests/fixtures/geo_rss_events.xml
vendored
76
tests/fixtures/geo_rss_events.xml
vendored
@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<rss version="2.0" xmlns:georss="http://www.georss.org/georss"
|
|
||||||
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
|
|
||||||
<channel>
|
|
||||||
<!-- Entry within vicinity of home coordinates - Point -->
|
|
||||||
<item>
|
|
||||||
<title>Title 1</title>
|
|
||||||
<description>Description 1</description>
|
|
||||||
<category>Category 1</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:00:00 UTC</pubDate>
|
|
||||||
<guid>GUID 1</guid>
|
|
||||||
<georss:point>-32.916667 151.75</georss:point>
|
|
||||||
</item>
|
|
||||||
<!-- Entry within vicinity of home coordinates - Point -->
|
|
||||||
<item>
|
|
||||||
<title>Title 2</title>
|
|
||||||
<description>Description 2</description>
|
|
||||||
<category>Category 2</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:05:00 GMT</pubDate>
|
|
||||||
<guid>GUID 2</guid>
|
|
||||||
<geo:long>148.601111</geo:long>
|
|
||||||
<geo:lat>-32.256944</geo:lat>
|
|
||||||
</item>
|
|
||||||
<!-- Entry within vicinity of home coordinates - Polygon -->
|
|
||||||
<item>
|
|
||||||
<title>Title 3</title>
|
|
||||||
<description>Description 3</description>
|
|
||||||
<category>Category 3</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:05:00 GMT</pubDate>
|
|
||||||
<guid>GUID 3</guid>
|
|
||||||
<georss:polygon>
|
|
||||||
-33.283333 149.1
|
|
||||||
-33.2999997 149.1
|
|
||||||
-33.2999997 149.1166663888889
|
|
||||||
-33.283333 149.1166663888889
|
|
||||||
-33.283333 149.1
|
|
||||||
</georss:polygon>
|
|
||||||
</item>
|
|
||||||
<!-- Entry out of vicinity of home coordinates - Point -->
|
|
||||||
<item>
|
|
||||||
<title>Title 4</title>
|
|
||||||
<description>Description 4</description>
|
|
||||||
<category>Category 4</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:15:00 GMT</pubDate>
|
|
||||||
<guid>GUID 4</guid>
|
|
||||||
<georss:point>52.518611 13.408333</georss:point>
|
|
||||||
</item>
|
|
||||||
<!-- Entry without coordinates -->
|
|
||||||
<item>
|
|
||||||
<title>Title 5</title>
|
|
||||||
<description>Description 5</description>
|
|
||||||
<category>Category 5</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:20:00 GMT</pubDate>
|
|
||||||
<guid>GUID 5</guid>
|
|
||||||
</item>
|
|
||||||
<!-- Entry within vicinity of home coordinates -->
|
|
||||||
<!-- Link instead of GUID; updated instead of pubDate -->
|
|
||||||
<item>
|
|
||||||
<title>Title 6</title>
|
|
||||||
<description>Description 6</description>
|
|
||||||
<category>Category 6</category>
|
|
||||||
<updated>2017-07-30T09:25:00.000Z</updated>
|
|
||||||
<link>Link 6</link>
|
|
||||||
<georss:point>-33.75801 150.70544</georss:point>
|
|
||||||
</item>
|
|
||||||
<!-- Entry with unsupported geometry - Line -->
|
|
||||||
<item>
|
|
||||||
<title>Title 1</title>
|
|
||||||
<description>Description 1</description>
|
|
||||||
<category>Category 1</category>
|
|
||||||
<pubDate>Sun, 30 Jul 2017 09:00:00 UTC</pubDate>
|
|
||||||
<guid>GUID 1</guid>
|
|
||||||
<georss:line>45.256 -110.45 46.46 -109.48 43.84 -109.86</georss:line>
|
|
||||||
</item>
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
Loading…
x
Reference in New Issue
Block a user