mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Make rmvtransport async (#17225)
* Make rmvtransport async * Make rmv transport async * Make async tests * Update rmvtransport module version * Remove unnecessary import * Make rmvtransport async * Make rmv transport async * Make async tests * Update rmvtransport module version * Remove unnecessary import * Update requirements * Remove async loop * Fix wrong import * Fix stupidness * Remove unnecessary import * Bump upstream version * Don't store the session * Refactor tests * Add test for no data * Fix linter issues * Fix stale docstring * Fix stale docstring * Remove unnecessary test code * Remove unnecessary import * Add configurable timeout * Remove global variable
This commit is contained in:
parent
8310f4a1cf
commit
cfc175d71d
@ -5,15 +5,18 @@ For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.rmvtransport/
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (CONF_NAME, ATTR_ATTRIBUTION)
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
REQUIREMENTS = ['PyRMVtransport==0.1']
|
||||
REQUIREMENTS = ['PyRMVtransport==0.1.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -26,6 +29,7 @@ CONF_LINES = 'lines'
|
||||
CONF_PRODUCTS = 'products'
|
||||
CONF_TIME_OFFSET = 'time_offset'
|
||||
CONF_MAX_JOURNEYS = 'max_journeys'
|
||||
CONF_TIMEOUT = 'timeout'
|
||||
|
||||
DEFAULT_NAME = 'RMV Journey'
|
||||
|
||||
@ -46,6 +50,8 @@ ICONS = {
|
||||
}
|
||||
ATTRIBUTION = "Data provided by opendata.rmv.de"
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_NEXT_DEPARTURE): [{
|
||||
vol.Required(CONF_STATION): cv.string,
|
||||
@ -59,16 +65,23 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]),
|
||||
vol.Optional(CONF_TIME_OFFSET, default=0): cv.positive_int,
|
||||
vol.Optional(CONF_MAX_JOURNEYS, default=5): cv.positive_int,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string}]
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string}],
|
||||
vol.Optional(CONF_TIMEOUT, default=10): cv.positive_int
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the RMV departure sensor."""
|
||||
timeout = config.get(CONF_TIMEOUT)
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
|
||||
sensors = []
|
||||
for next_departure in config.get(CONF_NEXT_DEPARTURE):
|
||||
sensors.append(
|
||||
RMVDepartureSensor(
|
||||
session,
|
||||
next_departure[CONF_STATION],
|
||||
next_departure.get(CONF_DESTINATIONS),
|
||||
next_departure.get(CONF_DIRECTIONS),
|
||||
@ -76,21 +89,23 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
next_departure.get(CONF_PRODUCTS),
|
||||
next_departure.get(CONF_TIME_OFFSET),
|
||||
next_departure.get(CONF_MAX_JOURNEYS),
|
||||
next_departure.get(CONF_NAME)))
|
||||
add_entities(sensors, True)
|
||||
next_departure.get(CONF_NAME),
|
||||
timeout))
|
||||
async_add_entities(sensors, True)
|
||||
|
||||
|
||||
class RMVDepartureSensor(Entity):
|
||||
"""Implementation of an RMV departure sensor."""
|
||||
|
||||
def __init__(self, station, destinations, directions,
|
||||
lines, products, time_offset, max_journeys, name):
|
||||
def __init__(self, session, station, destinations, directions, lines,
|
||||
products, time_offset, max_journeys, name, timeout):
|
||||
"""Initialize the sensor."""
|
||||
self._station = station
|
||||
self._name = name
|
||||
self._state = None
|
||||
self.data = RMVDepartureData(station, destinations, directions, lines,
|
||||
products, time_offset, max_journeys)
|
||||
self.data = RMVDepartureData(session, station, destinations,
|
||||
directions, lines, products, time_offset,
|
||||
max_journeys, timeout)
|
||||
self._icon = ICONS[None]
|
||||
|
||||
@property
|
||||
@ -134,9 +149,10 @@ class RMVDepartureSensor(Entity):
|
||||
"""Return the unit this state is expressed in."""
|
||||
return "min"
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Get the latest data and update the state."""
|
||||
self.data.update()
|
||||
await self.data.async_update()
|
||||
|
||||
if not self.data.departures:
|
||||
self._state = None
|
||||
self._icon = ICONS[None]
|
||||
@ -151,10 +167,11 @@ class RMVDepartureSensor(Entity):
|
||||
class RMVDepartureData:
|
||||
"""Pull data from the opendata.rmv.de web page."""
|
||||
|
||||
def __init__(self, station_id, destinations, directions,
|
||||
lines, products, time_offset, max_journeys):
|
||||
def __init__(self, session, station_id, destinations, directions, lines,
|
||||
products, time_offset, max_journeys, timeout):
|
||||
"""Initialize the sensor."""
|
||||
import RMVtransport
|
||||
from RMVtransport import RMVtransport
|
||||
|
||||
self.station = None
|
||||
self._station_id = station_id
|
||||
self._destinations = destinations
|
||||
@ -163,15 +180,16 @@ class RMVDepartureData:
|
||||
self._products = products
|
||||
self._time_offset = time_offset
|
||||
self._max_journeys = max_journeys
|
||||
self.rmv = RMVtransport.RMVtransport()
|
||||
self.rmv = RMVtransport(session, timeout)
|
||||
self.departures = []
|
||||
|
||||
def update(self):
|
||||
@Throttle(SCAN_INTERVAL)
|
||||
async def async_update(self):
|
||||
"""Update the connection data."""
|
||||
try:
|
||||
_data = self.rmv.get_departures(self._station_id,
|
||||
products=self._products,
|
||||
maxJourneys=50)
|
||||
_data = await self.rmv.get_departures(self._station_id,
|
||||
products=self._products,
|
||||
maxJourneys=50)
|
||||
except ValueError:
|
||||
self.departures = []
|
||||
_LOGGER.warning("Returned data not understood")
|
||||
|
@ -52,7 +52,7 @@ PyMata==2.14
|
||||
PyQRCode==1.2.1
|
||||
|
||||
# homeassistant.components.sensor.rmvtransport
|
||||
PyRMVtransport==0.1
|
||||
PyRMVtransport==0.1.3
|
||||
|
||||
# homeassistant.components.switch.switchbot
|
||||
PySwitchbot==0.3
|
||||
|
@ -22,7 +22,7 @@ requests_mock==1.5.2
|
||||
HAP-python==2.2.2
|
||||
|
||||
# homeassistant.components.sensor.rmvtransport
|
||||
PyRMVtransport==0.1
|
||||
PyRMVtransport==0.1.3
|
||||
|
||||
# homeassistant.components.notify.yessssms
|
||||
YesssSMS==0.2.3
|
||||
|
@ -1,14 +1,17 @@
|
||||
"""The tests for the rmvtransport platform."""
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.setup import setup_component
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import mock_coro
|
||||
|
||||
VALID_CONFIG_MINIMAL = {'sensor': {'platform': 'rmvtransport',
|
||||
'next_departure': [{'station': '3000010'}]}}
|
||||
|
||||
VALID_CONFIG_MINIMAL = {'sensor': {
|
||||
'platform': 'rmvtransport',
|
||||
'next_departure': [
|
||||
{'station': '3000010'}
|
||||
]}}
|
||||
|
||||
VALID_CONFIG_NAME = {'sensor': {
|
||||
'platform': 'rmvtransport',
|
||||
@ -41,8 +44,7 @@ VALID_CONFIG_DEST = {'sensor': {
|
||||
]}}
|
||||
|
||||
|
||||
def get_departuresMock(stationId, maxJourneys,
|
||||
products): # pylint: disable=invalid-name
|
||||
def get_departures_mock():
|
||||
"""Mock rmvtransport departures loading."""
|
||||
data = {'station': 'Frankfurt (Main) Hauptbahnhof',
|
||||
'stationId': '3000010', 'filter': '11111111111', 'journeys': [
|
||||
@ -97,77 +99,77 @@ def get_departuresMock(stationId, maxJourneys,
|
||||
return data
|
||||
|
||||
|
||||
def get_errDeparturesMock(stationId, maxJourneys,
|
||||
products): # pylint: disable=invalid-name
|
||||
"""Mock rmvtransport departures erroneous loading."""
|
||||
raise ValueError
|
||||
def get_no_departures_mock():
|
||||
"""Mock no departures in results."""
|
||||
data = {'station': 'Frankfurt (Main) Hauptbahnhof',
|
||||
'stationId': '3000010',
|
||||
'filter': '11111111111',
|
||||
'journeys': []}
|
||||
return data
|
||||
|
||||
|
||||
class TestRMVtransportSensor(unittest.TestCase):
|
||||
"""Test the rmvtransport sensor."""
|
||||
async def test_rmvtransport_min_config(hass):
|
||||
"""Test minimal rmvtransport configuration."""
|
||||
with patch('RMVtransport.RMVtransport.get_departures',
|
||||
return_value=mock_coro(get_departures_mock())):
|
||||
assert await async_setup_component(hass, 'sensor',
|
||||
VALID_CONFIG_MINIMAL) is True
|
||||
|
||||
def setUp(self):
|
||||
"""Set up things to run when tests begin."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.config = VALID_CONFIG_MINIMAL
|
||||
self.reference = {}
|
||||
self.entities = []
|
||||
state = hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
assert state.state == '7'
|
||||
assert state.attributes['departure_time'] == \
|
||||
datetime.datetime(2018, 8, 6, 14, 21)
|
||||
assert state.attributes['direction'] == \
|
||||
'Frankfurt (Main) Hugo-Junkers-Straße/Schleife'
|
||||
assert state.attributes['product'] == 'Tram'
|
||||
assert state.attributes['line'] == 12
|
||||
assert state.attributes['icon'] == 'mdi:tram'
|
||||
assert state.attributes['friendly_name'] == 'Frankfurt (Main) Hauptbahnhof'
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('RMVtransport.RMVtransport.get_departures',
|
||||
side_effect=get_departuresMock)
|
||||
def test_rmvtransport_min_config(self, mock_get_departures):
|
||||
"""Test minimal rmvtransport configuration."""
|
||||
assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL)
|
||||
state = self.hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
self.assertEqual(state.state, '7')
|
||||
self.assertEqual(state.attributes['departure_time'],
|
||||
datetime.datetime(2018, 8, 6, 14, 21))
|
||||
self.assertEqual(state.attributes['direction'],
|
||||
'Frankfurt (Main) Hugo-Junkers-Straße/Schleife')
|
||||
self.assertEqual(state.attributes['product'], 'Tram')
|
||||
self.assertEqual(state.attributes['line'], 12)
|
||||
self.assertEqual(state.attributes['icon'], 'mdi:tram')
|
||||
self.assertEqual(state.attributes['friendly_name'],
|
||||
'Frankfurt (Main) Hauptbahnhof')
|
||||
async def test_rmvtransport_name_config(hass):
|
||||
"""Test custom name configuration."""
|
||||
with patch('RMVtransport.RMVtransport.get_departures',
|
||||
return_value=mock_coro(get_departures_mock())):
|
||||
assert await async_setup_component(hass, 'sensor', VALID_CONFIG_NAME)
|
||||
|
||||
@patch('RMVtransport.RMVtransport.get_departures',
|
||||
side_effect=get_departuresMock)
|
||||
def test_rmvtransport_name_config(self, mock_get_departures):
|
||||
"""Test custom name configuration."""
|
||||
assert setup_component(self.hass, 'sensor', VALID_CONFIG_NAME)
|
||||
state = self.hass.states.get('sensor.my_station')
|
||||
self.assertEqual(state.attributes['friendly_name'], 'My Station')
|
||||
state = hass.states.get('sensor.my_station')
|
||||
assert state.attributes['friendly_name'] == 'My Station'
|
||||
|
||||
@patch('RMVtransport.RMVtransport.get_departures',
|
||||
side_effect=get_errDeparturesMock)
|
||||
def test_rmvtransport_err_config(self, mock_get_departures):
|
||||
"""Test erroneous rmvtransport configuration."""
|
||||
assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL)
|
||||
|
||||
@patch('RMVtransport.RMVtransport.get_departures',
|
||||
side_effect=get_departuresMock)
|
||||
def test_rmvtransport_misc_config(self, mock_get_departures):
|
||||
"""Test misc configuration."""
|
||||
assert setup_component(self.hass, 'sensor', VALID_CONFIG_MISC)
|
||||
state = self.hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
self.assertEqual(state.attributes['friendly_name'],
|
||||
'Frankfurt (Main) Hauptbahnhof')
|
||||
self.assertEqual(state.attributes['line'], 21)
|
||||
async def test_rmvtransport_misc_config(hass):
|
||||
"""Test misc configuration."""
|
||||
with patch('RMVtransport.RMVtransport.get_departures',
|
||||
return_value=mock_coro(get_departures_mock())):
|
||||
assert await async_setup_component(hass, 'sensor', VALID_CONFIG_MISC)
|
||||
|
||||
@patch('RMVtransport.RMVtransport.get_departures',
|
||||
side_effect=get_departuresMock)
|
||||
def test_rmvtransport_dest_config(self, mock_get_departures):
|
||||
"""Test misc configuration."""
|
||||
assert setup_component(self.hass, 'sensor', VALID_CONFIG_DEST)
|
||||
state = self.hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
self.assertEqual(state.state, '11')
|
||||
self.assertEqual(state.attributes['direction'],
|
||||
'Frankfurt (Main) Hugo-Junkers-Straße/Schleife')
|
||||
self.assertEqual(state.attributes['line'], 12)
|
||||
self.assertEqual(state.attributes['minutes'], 11)
|
||||
self.assertEqual(state.attributes['departure_time'],
|
||||
datetime.datetime(2018, 8, 6, 14, 25))
|
||||
state = hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
assert state.attributes['friendly_name'] == 'Frankfurt (Main) Hauptbahnhof'
|
||||
assert state.attributes['line'] == 21
|
||||
|
||||
|
||||
async def test_rmvtransport_dest_config(hass):
|
||||
"""Test destination configuration."""
|
||||
with patch('RMVtransport.RMVtransport.get_departures',
|
||||
return_value=mock_coro(get_departures_mock())):
|
||||
assert await async_setup_component(hass, 'sensor', VALID_CONFIG_DEST)
|
||||
|
||||
state = hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
assert state.state == '11'
|
||||
assert state.attributes['direction'] == \
|
||||
'Frankfurt (Main) Hugo-Junkers-Straße/Schleife'
|
||||
assert state.attributes['line'] == 12
|
||||
assert state.attributes['minutes'] == 11
|
||||
assert state.attributes['departure_time'] == \
|
||||
datetime.datetime(2018, 8, 6, 14, 25)
|
||||
|
||||
|
||||
async def test_rmvtransport_no_departures(hass):
|
||||
"""Test for no departures."""
|
||||
with patch('RMVtransport.RMVtransport.get_departures',
|
||||
return_value=mock_coro(get_no_departures_mock())):
|
||||
assert await async_setup_component(hass, 'sensor',
|
||||
VALID_CONFIG_MINIMAL)
|
||||
|
||||
state = hass.states.get('sensor.frankfurt_main_hauptbahnhof')
|
||||
assert not state
|
||||
|
Loading…
x
Reference in New Issue
Block a user