From c3f61e0eec29172b51eed9cf7240a169ff40e365 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 2 Aug 2018 15:58:11 +0200 Subject: [PATCH 01/27] Add new public transport sensor for RMV (Rhein-Main area). --- .../components/sensor/rmvtransport.py | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 homeassistant/components/sensor/rmvtransport.py diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py new file mode 100644 index 0000000000000..053b455572d96 --- /dev/null +++ b/homeassistant/components/sensor/rmvtransport.py @@ -0,0 +1,187 @@ +""" +Support for real-time departure information for Rhein-Main public transport. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.rmvdeparture/ +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN + ) + +# REQUIREMENTS = ['PyRMVtransport==0.0.1'] + +_LOGGER = logging.getLogger(__name__) + +CONF_NEXT_DEPARTURE = 'nextdeparture' + +CONF_STATION = 'station' +CONF_DESTINATIONS = 'destinations' +CONF_DIRECTIONS = 'directions' +CONF_LINES = 'lines' +CONF_PRODUCTS = 'products' +CONF_TIMEOFFSET = 'timeoffset' + +DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'S'] + +ICONS = { + 'U-Bahn': 'mdi:subway', + 'Tram': 'mdi:tram', + 'Bus': 'mdi:bus', + 'S': 'mdi:train', + 'RB': 'mdi:train', + 'RE': 'mdi:train', + 'EC': 'mdi:train', + 'IC': 'mdi:train', + 'ICE': 'mdi:train', + 'SEV': 'mdi:checkbox-blank-circle-outline', + '-': 'mdi:clock' +} +ATTRIBUTION = "Data provided by opendata.rmv.de" + +SCAN_INTERVAL = timedelta(seconds=30) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NEXT_DEPARTURE): [{ + vol.Required(CONF_STATION): cv.string, + vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list_csv, + vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, + vol.Optional(CONF_LINES, default=['']): cv.ensure_list_csv, + vol.Optional(CONF_PRODUCTS, default=DEFAULT_PRODUCT): + cv.ensure_list_csv, + vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, + vol.Optional(CONF_NAME): cv.string}] +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the RMV departure sensor.""" + sensors = [] + for nextdeparture in config.get(CONF_NEXT_DEPARTURE): + sensors.append( + RMVDepartureSensor( + nextdeparture.get(CONF_STATION), + nextdeparture.get(CONF_DESTINATIONS), + nextdeparture.get(CONF_DIRECTIONS), + nextdeparture.get(CONF_LINES), + nextdeparture.get(CONF_PRODUCTS), + nextdeparture.get(CONF_TIMEOFFSET), + nextdeparture.get(CONF_NAME))) + add_devices(sensors, True) + + +class RMVDepartureSensor(Entity): + """Implementation of an RMV departure sensor.""" + + def __init__(self, station, destinations, directions, + lines, products, timeoffset, name): + """Initialize the sensor.""" + self._station = station + self._name = name + self.data = RMVDepartureData(station, destinations, directions, + lines, products, timeoffset) + self._state = STATE_UNKNOWN + self._icon = ICONS['-'] + + @property + def name(self): + """Return the name of the sensor.""" + if self._name: + return self._name + return self._station + + @property + def state(self): + """Return the next departure time.""" + return self._state + + @property + def state_attributes(self): + """Return the state attributes.""" + # return self.data.departures + return { + 'next_departures': [val for val in self.data.departures[1:]], + 'direction': self.data.departures[0].get('direction', '-'), + 'number': self.data.departures[0].get('number', '-'), + 'minutes': self.data.departures[0].get('minutes', '-'), + 'product': self.data.departures[0].get('product', '-'), + } + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return self._icon + + @property + def unit_of_measurement(self): + """Return the unit this state is expressed in.""" + return "min" + + def update(self): + """Get the latest data and update the state.""" + self.data.update() + if not self.data.departures: + self._state = '-' + self._icon = ICONS['-'] + else: + self._name = self.data.station + self._state = self.data.departures[0].get('minutes', '-') + self._icon = ICONS[self.data.departures[0].get('product', '-')] + + +class RMVDepartureData: + """Pull data from the opendata.rmv.de web page.""" + + def __init__(self, stationId, destinations, directions, + lines, products, timeoffset): + """Initialize the sensor.""" + import RMVtransport + self.station = None + self._stationId = stationId + self._destinations = destinations + self._directions = directions + self._lines = lines + self._products = products + self._timeoffset = timeoffset + self.rmv = RMVtransport.RMVtransport() + self.departures = [] + + def update(self): + """Update the connection data.""" + try: + _data = self.rmv.get_departures(stationId=self._stationId, + products=self._products) + except ValueError: + self.departures = {} + _LOGGER.warning("Returned data not understood") + return + self.station = _data['station'] + _deps = [] + for journey in _data['journeys']: + # find the first departure meeting the criteria + _nextdep = {ATTR_ATTRIBUTION: ATTRIBUTION} + if '' not in self._destinations[:1]: + dest_found = False + for dest in self._destinations: + if dest in journey['stops']: + dest_found = True + _nextdep['destination'] = dest + if not dest_found: + continue + elif ('' not in self._lines[:1] and + journey['number'] not in self._lines): + continue + elif journey['minutes'] < self._timeoffset: + continue + for k in ['direction', 'number', 'minutes', + 'product']: + _nextdep[k] = journey.get(k, '') + _deps.append(_nextdep) + self.departures = _deps From b9b59aa6b89a9be930322080580c3824d703f487 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 2 Aug 2018 16:13:40 +0200 Subject: [PATCH 02/27] Add required module. --- homeassistant/components/sensor/rmvtransport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 053b455572d96..17ad3a75b2ca2 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -16,7 +16,7 @@ CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN ) -# REQUIREMENTS = ['PyRMVtransport==0.0.1'] +REQUIREMENTS = ['PyRMVtransport==0.0.4'] _LOGGER = logging.getLogger(__name__) From 41c34ea1d5f59e6be34b6a8db5b59bec8953cd57 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 3 Aug 2018 00:01:18 +0200 Subject: [PATCH 03/27] Fix naming problem. --- .../components/sensor/rmvtransport.py | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 17ad3a75b2ca2..4905e08eea387 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -28,8 +28,11 @@ CONF_LINES = 'lines' CONF_PRODUCTS = 'products' CONF_TIMEOFFSET = 'timeoffset' +CONF_MAXJOURNEYS = 'max' -DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'S'] +DEFAULT_NAME = 'RMV Journey' + +DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'S', 'RB', 'RE', 'EC', 'IC', 'ICE'] ICONS = { 'U-Bahn': 'mdi:subway', @@ -57,7 +60,8 @@ vol.Optional(CONF_PRODUCTS, default=DEFAULT_PRODUCT): cv.ensure_list_csv, vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, - vol.Optional(CONF_NAME): cv.string}] + vol.Optional(CONF_MAXJOURNEYS, default=5): cv.positive_int, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string}] }) @@ -73,6 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): nextdeparture.get(CONF_LINES), nextdeparture.get(CONF_PRODUCTS), nextdeparture.get(CONF_TIMEOFFSET), + nextdeparture.get(CONF_MAXJOURNEYS), nextdeparture.get(CONF_NAME))) add_devices(sensors, True) @@ -81,21 +86,19 @@ class RMVDepartureSensor(Entity): """Implementation of an RMV departure sensor.""" def __init__(self, station, destinations, directions, - lines, products, timeoffset, name): + lines, products, timeoffset, maxjourneys, name): """Initialize the sensor.""" self._station = station self._name = name self.data = RMVDepartureData(station, destinations, directions, - lines, products, timeoffset) + lines, products, timeoffset, maxjourneys) self._state = STATE_UNKNOWN self._icon = ICONS['-'] @property def name(self): """Return the name of the sensor.""" - if self._name: - return self._name - return self._station + return self._name @property def state(self): @@ -105,11 +108,10 @@ def state(self): @property def state_attributes(self): """Return the state attributes.""" - # return self.data.departures return { 'next_departures': [val for val in self.data.departures[1:]], 'direction': self.data.departures[0].get('direction', '-'), - 'number': self.data.departures[0].get('number', '-'), + 'line': self.data.departures[0].get('number', '-'), 'minutes': self.data.departures[0].get('minutes', '-'), 'product': self.data.departures[0].get('product', '-'), } @@ -131,7 +133,11 @@ def update(self): self._state = '-' self._icon = ICONS['-'] else: - self._name = self.data.station + if self._name is DEFAULT_NAME: + self._name = self.data.station + else: + self._name = self._name + self._station = self.data.station self._state = self.data.departures[0].get('minutes', '-') self._icon = ICONS[self.data.departures[0].get('product', '-')] @@ -140,7 +146,7 @@ class RMVDepartureData: """Pull data from the opendata.rmv.de web page.""" def __init__(self, stationId, destinations, directions, - lines, products, timeoffset): + lines, products, timeoffset, maxjourneys): """Initialize the sensor.""" import RMVtransport self.station = None @@ -150,6 +156,7 @@ def __init__(self, stationId, destinations, directions, self._lines = lines self._products = products self._timeoffset = timeoffset + self._maxjourneys = maxjourneys self.rmv = RMVtransport.RMVtransport() self.departures = [] @@ -157,7 +164,8 @@ def update(self): """Update the connection data.""" try: _data = self.rmv.get_departures(stationId=self._stationId, - products=self._products) + products=self._products, + maxJourneys=50) except ValueError: self.departures = {} _LOGGER.warning("Returned data not understood") @@ -184,4 +192,6 @@ def update(self): 'product']: _nextdep[k] = journey.get(k, '') _deps.append(_nextdep) + if len(_deps) > self._maxjourneys: + break self.departures = _deps From f9e88b6fc6b0a24fdcc8eb3631cac1b4294ad6e5 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 3 Aug 2018 22:34:44 +0200 Subject: [PATCH 04/27] Add unit test. --- .../components/sensor/rmvtransport.py | 34 ++-- script/gen_requirements_all.py | 3 +- tests/components/sensor/test_rmvtransport.py | 169 ++++++++++++++++++ 3 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 tests/components/sensor/test_rmvtransport.py diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 4905e08eea387..6a1fbc23e7f6a 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -52,7 +52,7 @@ SCAN_INTERVAL = timedelta(seconds=30) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NEXT_DEPARTURE): [{ + vol.Required(CONF_NEXT_DEPARTURE): [{ vol.Required(CONF_STATION): cv.string, vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list_csv, vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, @@ -65,7 +65,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RMV departure sensor.""" sensors = [] for nextdeparture in config.get(CONF_NEXT_DEPARTURE): @@ -79,7 +79,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): nextdeparture.get(CONF_TIMEOFFSET), nextdeparture.get(CONF_MAXJOURNEYS), nextdeparture.get(CONF_NAME))) - add_devices(sensors, True) + add_entities(sensors, True) class RMVDepartureSensor(Entity): @@ -100,6 +100,13 @@ def name(self): """Return the name of the sensor.""" return self._name + @property + def available(self): + """Return True if entity is available.""" + if self._state is STATE_UNKNOWN: + return False + return True + @property def state(self): """Return the next departure time.""" @@ -108,13 +115,18 @@ def state(self): @property def state_attributes(self): """Return the state attributes.""" - return { - 'next_departures': [val for val in self.data.departures[1:]], - 'direction': self.data.departures[0].get('direction', '-'), - 'line': self.data.departures[0].get('number', '-'), - 'minutes': self.data.departures[0].get('minutes', '-'), - 'product': self.data.departures[0].get('product', '-'), - } + result = {} + try: + result = { + 'next_departures': [val for val in self.data.departures[1:]], + 'direction': self.data.departures[0].get('direction', '-'), + 'line': self.data.departures[0].get('number', '-'), + 'minutes': self.data.departures[0].get('minutes', '-'), + 'product': self.data.departures[0].get('product', '-'), + } + except IndexError: + pass + return result @property def icon(self): @@ -170,7 +182,7 @@ def update(self): self.departures = {} _LOGGER.warning("Returned data not understood") return - self.station = _data['station'] + self.station = _data.get('station', '-') _deps = [] for journey in _data['journeys']: # find the first departure meeting the criteria diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index d92502de0782f..055fb480349a0 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -98,7 +98,8 @@ 'yahoo-finance', 'pythonwhois', 'wakeonlan', - 'vultr' + 'vultr', + 'PyRMVtransport' ) IGNORE_PACKAGES = ( diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py new file mode 100644 index 0000000000000..2bb0dc47efcba --- /dev/null +++ b/tests/components/sensor/test_rmvtransport.py @@ -0,0 +1,169 @@ +"""The tests for the rmvtransport platform.""" +import unittest +from unittest.mock import patch + +from homeassistant.setup import setup_component + +from tests.common import get_test_home_assistant + +VALID_CONFIG_MINIMAL = {'sensor': {'platform': 'rmvtransport', + 'nextdeparture': [{'station': '3000010'}]}} + +VALID_CONFIG_NAME = {'sensor': { + 'platform': 'rmvtransport', + 'nextdeparture': [ + { + 'station': '3000010', + 'name': 'My Station', + } + ]}} + +VALID_CONFIG_MISC = {'sensor': { + 'platform': 'rmvtransport', + 'nextdeparture': [ + { + 'station': '3000010', + 'lines': [21], + 'max': 2, + 'timeoffset': 10 + } + ]}} + +VALID_CONFIG_DEST = {'sensor': { + 'platform': 'rmvtransport', + 'nextdeparture': [ + { + 'station': '3000010', + 'destinations': ['Frankfurt (Main) Flughafen Regionalbahnhof', + 'Frankfurt (Main) Stadion'] + } + ]}} + + +def get_departuresMock(stationId, maxJourneys, + products): # pylint: disable=invalid-name + """Mock rmvtransport departures loading.""" + data = {'station': 'Frankfurt (Main) Hauptbahnhof', + 'stationId': '3000010', 'filter': '11111111111', 'journeys': [ + {'product': 'Tram', 'number': 12, 'trainId': '1123456', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 7, 'delay': 3, 'stops': [ + 'Frankfurt (Main) Willy-Brandt-Platz', + 'Frankfurt (Main) Römer/Paulskirche', + 'Frankfurt (Main) Börneplatz', + 'Frankfurt (Main) Konstablerwache', + 'Frankfurt (Main) Bornheim Mitte', + 'Frankfurt (Main) Saalburg-/Wittelsbacherallee', + 'Frankfurt (Main) Eissporthalle/Festplatz', + 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife'], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'}, + {'product': 'Bus', 'number': 21, 'trainId': '1234567', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 8, 'delay': 1, 'stops': [ + 'Frankfurt (Main) Weser-/Münchener Straße', + 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife'], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'}, + {'product': 'Bus', 'number': 12, 'trainId': '1234568', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 11, 'delay': 1, 'stops': [ + 'Frankfurt (Main) Stadion'], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'}, + {'product': 'Bus', 'number': 21, 'trainId': '1234569', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 11, 'delay': 1, 'stops': [], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'}, + {'product': 'Bus', 'number': 12, 'trainId': '1234570', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 11, 'delay': 1, 'stops': [], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'}, + {'product': 'Bus', 'number': 21, 'trainId': '1234571', + 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'minutes': 11, 'delay': 1, 'stops': [], + 'info': None, 'info_long': None, + 'icon': 'https://products/32_pic.png'} + ] + } + return data + + +def get_errDeparturesMock(stationId, maxJourneys, + products): # pylint: disable=invalid-name + """Mock rmvtransport departures erroneous loading.""" + raise ValueError + + +class TestRMVtransportSensor(unittest.TestCase): + """Test the rmvtransport sensor.""" + + 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 = [] + + 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') + print("state: ", repr(state)) + + print("ref: ", state.attributes) + + self.assertEqual(state.state, '7') + self.assertEqual(state.attributes['minutes'], 7) + 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') + + @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') + + @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) + + @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') + print("ref: ", state.attributes) + self.assertEqual(state.attributes['direction'], + 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife') + self.assertEqual(state.attributes['line'], 12) + self.assertEqual(state.attributes['minutes'], 11) From 221167ebc661b7e5b625f2a3223d79a746c144ff Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 3 Aug 2018 22:40:13 +0200 Subject: [PATCH 05/27] Update dependency version to 0.0.5. --- homeassistant/components/sensor/rmvtransport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 6a1fbc23e7f6a..55daa9d3fcb86 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -16,7 +16,7 @@ CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN ) -REQUIREMENTS = ['PyRMVtransport==0.0.4'] +REQUIREMENTS = ['PyRMVtransport==0.0.5'] _LOGGER = logging.getLogger(__name__) From 504049bd5c5883daa131fe0543e8bc96d40a142f Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 3 Aug 2018 22:48:13 +0200 Subject: [PATCH 06/27] Add new requirements. --- requirements_all.txt | 3 +++ requirements_test_all.txt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/requirements_all.txt b/requirements_all.txt index 51ab8100372a3..2e1458bcfa4ea 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -47,6 +47,9 @@ PyMVGLive==1.1.4 # homeassistant.components.arduino PyMata==2.14 +# homeassistant.components.sensor.rmvtransport +PyRMVtransport==0.0.5 + # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 913117adc2dc2..6f0ce0f86f124 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -24,6 +24,9 @@ HAP-python==2.2.2 # homeassistant.components.notify.html5 PyJWT==1.6.0 +# homeassistant.components.sensor.rmvtransport +PyRMVtransport==0.0.5 + # homeassistant.components.sonos SoCo==0.14 From db76dd046ea0a3b0611b26e1ec3fe7cd42d88e99 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 3 Aug 2018 23:15:11 +0200 Subject: [PATCH 07/27] Fix variable name. --- homeassistant/components/sensor/rmvtransport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 55daa9d3fcb86..2a4650fe1a19c 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -157,12 +157,12 @@ def update(self): class RMVDepartureData: """Pull data from the opendata.rmv.de web page.""" - def __init__(self, stationId, destinations, directions, + def __init__(self, station_id, destinations, directions, lines, products, timeoffset, maxjourneys): """Initialize the sensor.""" import RMVtransport self.station = None - self._stationId = stationId + self._station_id = station_id self._destinations = destinations self._directions = directions self._lines = lines @@ -175,7 +175,7 @@ def __init__(self, stationId, destinations, directions, def update(self): """Update the connection data.""" try: - _data = self.rmv.get_departures(stationId=self._stationId, + _data = self.rmv.get_departures(self._station_id, products=self._products, maxJourneys=50) except ValueError: From 2d9c122576bc145c5510c0160e491af5a89a06b5 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 5 Aug 2018 20:47:08 +0200 Subject: [PATCH 08/27] Fix issues pointed out in review. --- .../components/sensor/rmvtransport.py | 34 +++++++++---------- script/gen_requirements_all.py | 4 +-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 2a4650fe1a19c..ec3f292965e53 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -45,7 +45,7 @@ 'IC': 'mdi:train', 'ICE': 'mdi:train', 'SEV': 'mdi:checkbox-blank-circle-outline', - '-': 'mdi:clock' + None: 'mdi:clock' } ATTRIBUTION = "Data provided by opendata.rmv.de" @@ -58,7 +58,7 @@ vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, vol.Optional(CONF_LINES, default=['']): cv.ensure_list_csv, vol.Optional(CONF_PRODUCTS, default=DEFAULT_PRODUCT): - cv.ensure_list_csv, + vol.All(cv.ensure_list, [vol.In(DEFAULT_PRODUCT)]), vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, vol.Optional(CONF_MAXJOURNEYS, default=5): cv.positive_int, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string}] @@ -93,7 +93,7 @@ def __init__(self, station, destinations, directions, self.data = RMVDepartureData(station, destinations, directions, lines, products, timeoffset, maxjourneys) self._state = STATE_UNKNOWN - self._icon = ICONS['-'] + self._icon = ICONS[None] @property def name(self): @@ -103,9 +103,7 @@ def name(self): @property def available(self): """Return True if entity is available.""" - if self._state is STATE_UNKNOWN: - return False - return True + return self._state != STATE_UNKNOWN @property def state(self): @@ -119,10 +117,10 @@ def state_attributes(self): try: result = { 'next_departures': [val for val in self.data.departures[1:]], - 'direction': self.data.departures[0].get('direction', '-'), - 'line': self.data.departures[0].get('number', '-'), - 'minutes': self.data.departures[0].get('minutes', '-'), - 'product': self.data.departures[0].get('product', '-'), + 'direction': self.data.departures[0].get('direction', None), + 'line': self.data.departures[0].get('number', None), + 'minutes': self.data.departures[0].get('minutes', None), + 'product': self.data.departures[0].get('product', None), } except IndexError: pass @@ -142,16 +140,16 @@ def update(self): """Get the latest data and update the state.""" self.data.update() if not self.data.departures: - self._state = '-' - self._icon = ICONS['-'] + self._state = None + self._icon = ICONS[None] + return else: - if self._name is DEFAULT_NAME: + if self._name == DEFAULT_NAME: self._name = self.data.station - else: - self._name = self._name self._station = self.data.station - self._state = self.data.departures[0].get('minutes', '-') - self._icon = ICONS[self.data.departures[0].get('product', '-')] + self._state = self.data.departures[0].get('minutes', None) + self._icon = ICONS[self.data.departures[0].get('product', None)] + return class RMVDepartureData: @@ -182,7 +180,7 @@ def update(self): self.departures = {} _LOGGER.warning("Returned data not understood") return - self.station = _data.get('station', '-') + self.station = _data.get('station', None) _deps = [] for journey in _data['journeys']: # find the first departure meeting the criteria diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 055fb480349a0..d95b6ac6e64a8 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -77,6 +77,7 @@ 'pymonoprice', 'pynx584', 'pyqwikswitch', + 'PyRMVtransport', 'python-forecastio', 'python-nest', 'pytradfri\[async\]', @@ -98,8 +99,7 @@ 'yahoo-finance', 'pythonwhois', 'wakeonlan', - 'vultr', - 'PyRMVtransport' + 'vultr' ) IGNORE_PACKAGES = ( From 485ae89eb5c66177baddeef3549e51f51bccee7a Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 5 Aug 2018 20:59:58 +0200 Subject: [PATCH 09/27] Remove unnecessary code. --- homeassistant/components/sensor/rmvtransport.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index ec3f292965e53..29afc9048febc 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -117,10 +117,10 @@ def state_attributes(self): try: result = { 'next_departures': [val for val in self.data.departures[1:]], - 'direction': self.data.departures[0].get('direction', None), - 'line': self.data.departures[0].get('number', None), - 'minutes': self.data.departures[0].get('minutes', None), - 'product': self.data.departures[0].get('product', None), + 'direction': self.data.departures[0].get('direction'), + 'line': self.data.departures[0].get('number'), + 'minutes': self.data.departures[0].get('minutes'), + 'product': self.data.departures[0].get('product'), } except IndexError: pass From 6fcc48df461fadd0b9d54f4cb7ce1644c294e165 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 5 Aug 2018 23:20:33 +0200 Subject: [PATCH 10/27] Fix linter error. --- homeassistant/components/sensor/rmvtransport.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 29afc9048febc..5560f9a2ff933 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -143,13 +143,12 @@ def update(self): self._state = None self._icon = ICONS[None] return - else: - if self._name == DEFAULT_NAME: - self._name = self.data.station - self._station = self.data.station - self._state = self.data.departures[0].get('minutes', None) - self._icon = ICONS[self.data.departures[0].get('product', None)] - return + if self._name == DEFAULT_NAME: + self._name = self.data.station + self._station = self.data.station + self._state = self.data.departures[0].get('minutes', None) + self._icon = ICONS[self.data.departures[0].get('product', None)] + return class RMVDepartureData: From 741a711bc1a3b72aa83f92fa955d5669b0cdc382 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Mon, 6 Aug 2018 09:05:06 +0200 Subject: [PATCH 11/27] Fix config value validation. --- homeassistant/components/sensor/rmvtransport.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 5560f9a2ff933..84de33c110282 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -32,7 +32,18 @@ DEFAULT_NAME = 'RMV Journey' -DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'S', 'RB', 'RE', 'EC', 'IC', 'ICE'] +VALID_PRODUCTS = { + 'U-Bahn': ['U-Bahn'], + 'Tram': ['Tram'], + 'Bus': ['Bus'], + 'S': ['S'], + 'RB': ['RB'], + 'RE': ['RE'], + 'EC': ['EC'], + 'IC': ['IC'], + 'ICE': ['ICE'] +} +DEFAULT_PRODUCT = list(VALID_PRODUCTS.keys()) ICONS = { 'U-Bahn': 'mdi:subway', @@ -58,7 +69,7 @@ vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, vol.Optional(CONF_LINES, default=['']): cv.ensure_list_csv, vol.Optional(CONF_PRODUCTS, default=DEFAULT_PRODUCT): - vol.All(cv.ensure_list, [vol.In(DEFAULT_PRODUCT)]), + vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]), vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, vol.Optional(CONF_MAXJOURNEYS, default=5): cv.positive_int, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string}] From 140329320637d7b1949ade7eae02778803ac0b6a Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Mon, 6 Aug 2018 21:57:44 +0200 Subject: [PATCH 12/27] Replace minutes as state by departure timestamp. (see ##14983) --- .../components/sensor/rmvtransport.py | 10 +++++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/sensor/test_rmvtransport.py | 21 ++++++++++++------- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 84de33c110282..154aaf693daca 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -16,7 +16,7 @@ CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN ) -REQUIREMENTS = ['PyRMVtransport==0.0.5'] +REQUIREMENTS = ['PyRMVtransport==0.0.6'] _LOGGER = logging.getLogger(__name__) @@ -119,6 +119,7 @@ def available(self): @property def state(self): """Return the next departure time.""" + self._state = self.data.departures[0].get('departure_time', None) return self._state @property @@ -131,6 +132,8 @@ def state_attributes(self): 'direction': self.data.departures[0].get('direction'), 'line': self.data.departures[0].get('number'), 'minutes': self.data.departures[0].get('minutes'), + 'departure_time': + self.data.departures[0].get('departure_time'), 'product': self.data.departures[0].get('product'), } except IndexError: @@ -157,7 +160,7 @@ def update(self): if self._name == DEFAULT_NAME: self._name = self.data.station self._station = self.data.station - self._state = self.data.departures[0].get('minutes', None) + self._state = self.data.departures[0].get('departure_time', None) self._icon = ICONS[self.data.departures[0].get('product', None)] return @@ -208,8 +211,7 @@ def update(self): continue elif journey['minutes'] < self._timeoffset: continue - for k in ['direction', 'number', 'minutes', - 'product']: + for k in ['direction', 'number', 'departure_time', 'product']: _nextdep[k] = journey.get(k, '') _deps.append(_nextdep) if len(_deps) > self._maxjourneys: diff --git a/requirements_all.txt b/requirements_all.txt index 2e1458bcfa4ea..45fd8193b0e66 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -48,7 +48,7 @@ PyMVGLive==1.1.4 PyMata==2.14 # homeassistant.components.sensor.rmvtransport -PyRMVtransport==0.0.5 +PyRMVtransport==0.0.6 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6f0ce0f86f124..8c842ecf15705 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -25,7 +25,7 @@ HAP-python==2.2.2 PyJWT==1.6.0 # homeassistant.components.sensor.rmvtransport -PyRMVtransport==0.0.5 +PyRMVtransport==0.0.6 # homeassistant.components.sonos SoCo==0.14 diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py index 2bb0dc47efcba..cc6f6000dfa62 100644 --- a/tests/components/sensor/test_rmvtransport.py +++ b/tests/components/sensor/test_rmvtransport.py @@ -1,6 +1,7 @@ """The tests for the rmvtransport platform.""" import unittest from unittest.mock import patch +import datetime from homeassistant.setup import setup_component @@ -47,6 +48,7 @@ def get_departuresMock(stationId, maxJourneys, 'stationId': '3000010', 'filter': '11111111111', 'journeys': [ {'product': 'Tram', 'number': 12, 'trainId': '1123456', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 21), 'minutes': 7, 'delay': 3, 'stops': [ 'Frankfurt (Main) Willy-Brandt-Platz', 'Frankfurt (Main) Römer/Paulskirche', @@ -60,6 +62,7 @@ def get_departuresMock(stationId, maxJourneys, 'icon': 'https://products/32_pic.png'}, {'product': 'Bus', 'number': 21, 'trainId': '1234567', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 22), 'minutes': 8, 'delay': 1, 'stops': [ 'Frankfurt (Main) Weser-/Münchener Straße', 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife'], @@ -67,27 +70,30 @@ def get_departuresMock(stationId, maxJourneys, 'icon': 'https://products/32_pic.png'}, {'product': 'Bus', 'number': 12, 'trainId': '1234568', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 25), 'minutes': 11, 'delay': 1, 'stops': [ 'Frankfurt (Main) Stadion'], 'info': None, 'info_long': None, 'icon': 'https://products/32_pic.png'}, {'product': 'Bus', 'number': 21, 'trainId': '1234569', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 25), 'minutes': 11, 'delay': 1, 'stops': [], 'info': None, 'info_long': None, 'icon': 'https://products/32_pic.png'}, {'product': 'Bus', 'number': 12, 'trainId': '1234570', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 25), 'minutes': 11, 'delay': 1, 'stops': [], 'info': None, 'info_long': None, 'icon': 'https://products/32_pic.png'}, {'product': 'Bus', 'number': 21, 'trainId': '1234571', 'direction': 'Frankfurt (Main) Hugo-Junkers-Straße/Schleife', + 'departure_time': datetime.datetime(2018, 8, 6, 14, 25), 'minutes': 11, 'delay': 1, 'stops': [], 'info': None, 'info_long': None, 'icon': 'https://products/32_pic.png'} - ] - } + ]} return data @@ -118,12 +124,10 @@ def test_rmvtransport_min_config(self, mock_get_departures): assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL) state = self.hass.states.get('sensor.frankfurt_main_hauptbahnhof') - print("state: ", repr(state)) - print("ref: ", state.attributes) - - self.assertEqual(state.state, '7') - self.assertEqual(state.attributes['minutes'], 7) + self.assertEqual(state.state, '2018-08-06 14:21:00') + 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') @@ -166,4 +170,5 @@ def test_rmvtransport_dest_config(self, mock_get_departures): 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)) From f67f22ed91a97aaa235ff99ff5d394bfe1c31b11 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Tue, 7 Aug 2018 08:36:45 +0200 Subject: [PATCH 13/27] More work on the timestamp. (see ##14983) --- homeassistant/components/sensor/__init__.py | 3 ++- homeassistant/components/sensor/rmvtransport.py | 17 ++++++++++------- homeassistant/const.py | 1 + 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 8550d175b6333..a2d11a11dca42 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -14,7 +14,7 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.const import ( DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, - DEVICE_CLASS_TEMPERATURE) + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP) _LOGGER = logging.getLogger(__name__) @@ -28,6 +28,7 @@ DEVICE_CLASS_HUMIDITY, # % of humidity in the air DEVICE_CLASS_ILLUMINANCE, # current light level (lx/lm) DEVICE_CLASS_TEMPERATURE, # temperature (C/F) + DEVICE_CLASS_TIMESTAMP, # ISO formatted timestamp ] DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 154aaf693daca..c3e75f1728aef 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN + CONF_NAME, ATTR_ATTRIBUTION, DEVICE_CLASS_TIMESTAMP, STATE_UNKNOWN ) REQUIREMENTS = ['PyRMVtransport==0.0.6'] @@ -130,8 +130,7 @@ def state_attributes(self): result = { 'next_departures': [val for val in self.data.departures[1:]], 'direction': self.data.departures[0].get('direction'), - 'line': self.data.departures[0].get('number'), - 'minutes': self.data.departures[0].get('minutes'), + 'line': self.data.departures[0].get('line'), 'departure_time': self.data.departures[0].get('departure_time'), 'product': self.data.departures[0].get('product'), @@ -146,9 +145,12 @@ def icon(self): return self._icon @property - def unit_of_measurement(self): - """Return the unit this state is expressed in.""" - return "min" + def device_class(self): + """Return the device class of the sensor.""" + return DEVICE_CLASS_TIMESTAMP + # def unit_of_measurement(self): + # """Return the unit this state is expressed in.""" + # return "min" def update(self): """Get the latest data and update the state.""" @@ -211,8 +213,9 @@ def update(self): continue elif journey['minutes'] < self._timeoffset: continue - for k in ['direction', 'number', 'departure_time', 'product']: + for k in ['direction', 'departure_time', 'product']: _nextdep[k] = journey.get(k, '') + _nextdep['line'] = journey.get('number', '') _deps.append(_nextdep) if len(_deps) > self._maxjourneys: break diff --git a/homeassistant/const.py b/homeassistant/const.py index a84c278350f55..2361cd24a438a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -171,6 +171,7 @@ DEVICE_CLASS_HUMIDITY = 'humidity' DEVICE_CLASS_ILLUMINANCE = 'illuminance' DEVICE_CLASS_TEMPERATURE = 'temperature' +DEVICE_CLASS_TIMESTAMP = 'timestamp' # #### STATES #### STATE_ON = 'on' From 74117c371a2bde40c001337c7f0a85679bbbdc78 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Tue, 7 Aug 2018 21:51:08 +0200 Subject: [PATCH 14/27] Revert timestamp work until #14983 gets merged. --- homeassistant/components/sensor/__init__.py | 3 +-- homeassistant/components/sensor/rmvtransport.py | 17 ++++++++--------- homeassistant/const.py | 1 - tests/components/sensor/test_rmvtransport.py | 7 +++---- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index a2d11a11dca42..8550d175b6333 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -14,7 +14,7 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.const import ( DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP) + DEVICE_CLASS_TEMPERATURE) _LOGGER = logging.getLogger(__name__) @@ -28,7 +28,6 @@ DEVICE_CLASS_HUMIDITY, # % of humidity in the air DEVICE_CLASS_ILLUMINANCE, # current light level (lx/lm) DEVICE_CLASS_TEMPERATURE, # temperature (C/F) - DEVICE_CLASS_TIMESTAMP, # ISO formatted timestamp ] DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index c3e75f1728aef..4b5eb28e4b3cb 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_NAME, ATTR_ATTRIBUTION, DEVICE_CLASS_TIMESTAMP, STATE_UNKNOWN + CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN ) REQUIREMENTS = ['PyRMVtransport==0.0.6'] @@ -119,7 +119,7 @@ def available(self): @property def state(self): """Return the next departure time.""" - self._state = self.data.departures[0].get('departure_time', None) + self._state = self.data.departures[0].get('minutes', None) return self._state @property @@ -131,6 +131,7 @@ def state_attributes(self): 'next_departures': [val for val in self.data.departures[1:]], 'direction': self.data.departures[0].get('direction'), 'line': self.data.departures[0].get('line'), + 'minutes': self.data.departures[0].get('minutes'), 'departure_time': self.data.departures[0].get('departure_time'), 'product': self.data.departures[0].get('product'), @@ -145,12 +146,9 @@ def icon(self): return self._icon @property - def device_class(self): - """Return the device class of the sensor.""" - return DEVICE_CLASS_TIMESTAMP - # def unit_of_measurement(self): - # """Return the unit this state is expressed in.""" - # return "min" + def unit_of_measurement(self): + """Return the unit this state is expressed in.""" + return "min" def update(self): """Get the latest data and update the state.""" @@ -162,6 +160,7 @@ def update(self): if self._name == DEFAULT_NAME: self._name = self.data.station self._station = self.data.station + self._state = self.data.departures[0].get('minutes', None) self._state = self.data.departures[0].get('departure_time', None) self._icon = ICONS[self.data.departures[0].get('product', None)] return @@ -213,7 +212,7 @@ def update(self): continue elif journey['minutes'] < self._timeoffset: continue - for k in ['direction', 'departure_time', 'product']: + for k in ['direction', 'departure_time', 'product', 'minutes']: _nextdep[k] = journey.get(k, '') _nextdep['line'] = journey.get('number', '') _deps.append(_nextdep) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2361cd24a438a..a84c278350f55 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -171,7 +171,6 @@ DEVICE_CLASS_HUMIDITY = 'humidity' DEVICE_CLASS_ILLUMINANCE = 'illuminance' DEVICE_CLASS_TEMPERATURE = 'temperature' -DEVICE_CLASS_TIMESTAMP = 'timestamp' # #### STATES #### STATE_ON = 'on' diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py index cc6f6000dfa62..693a0cdac6ebe 100644 --- a/tests/components/sensor/test_rmvtransport.py +++ b/tests/components/sensor/test_rmvtransport.py @@ -122,10 +122,8 @@ def tearDown(self): 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, '2018-08-06 14:21:00') + self.assertEqual(state.state, '7') self.assertEqual(state.attributes['departure_time'], datetime.datetime(2018, 8, 6, 14, 21)) self.assertEqual(state.attributes['direction'], @@ -166,9 +164,10 @@ 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') - print("ref: ", state.attributes) + 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)) From 2accb78d3ecb15ec1fd1a826863f54beec7dbcbe Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Tue, 7 Aug 2018 22:19:34 +0200 Subject: [PATCH 15/27] Simplify product validation. --- homeassistant/components/sensor/rmvtransport.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 4b5eb28e4b3cb..cb5c721d5b2de 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -32,18 +32,7 @@ DEFAULT_NAME = 'RMV Journey' -VALID_PRODUCTS = { - 'U-Bahn': ['U-Bahn'], - 'Tram': ['Tram'], - 'Bus': ['Bus'], - 'S': ['S'], - 'RB': ['RB'], - 'RE': ['RE'], - 'EC': ['EC'], - 'IC': ['IC'], - 'ICE': ['ICE'] -} -DEFAULT_PRODUCT = list(VALID_PRODUCTS.keys()) +VALID_PRODUCTS = ['U-Bahn', 'Tram', 'Bus', 'S', 'RB', 'RE', 'EC', 'IC', 'ICE'] ICONS = { 'U-Bahn': 'mdi:subway', @@ -68,7 +57,7 @@ vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list_csv, vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, vol.Optional(CONF_LINES, default=['']): cv.ensure_list_csv, - vol.Optional(CONF_PRODUCTS, default=DEFAULT_PRODUCT): + vol.Optional(CONF_PRODUCTS, default=VALID_PRODUCTS): vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]), vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, vol.Optional(CONF_MAXJOURNEYS, default=5): cv.positive_int, From 661bad18a7c5ccd158303b8be279eeeb6435c02b Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Tue, 7 Aug 2018 22:57:15 +0200 Subject: [PATCH 16/27] Remove redundant code. --- homeassistant/components/sensor/rmvtransport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index cb5c721d5b2de..eb1d6cb627374 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -149,9 +149,9 @@ def update(self): if self._name == DEFAULT_NAME: self._name = self.data.station self._station = self.data.station - self._state = self.data.departures[0].get('minutes', None) - self._state = self.data.departures[0].get('departure_time', None) - self._icon = ICONS[self.data.departures[0].get('product', None)] + self._state = self.data.departures[0].get('minutes') + self._state = self.data.departures[0].get('departure_time') + self._icon = ICONS[self.data.departures[0].get('product')] return From 26f12270c0687aeabeb9966bfd171d8c3511f4ec Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 21:11:32 +0200 Subject: [PATCH 17/27] Address code change requests. --- .../components/sensor/rmvtransport.py | 49 +++++++++---------- tests/components/sensor/test_rmvtransport.py | 8 +-- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index eb1d6cb627374..d786b25c7046f 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -2,7 +2,7 @@ Support for real-time departure information for Rhein-Main public transport. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rmvdeparture/ +https://home-assistant.io/components/sensor.rmvtransport/ """ import logging from datetime import timedelta @@ -20,15 +20,15 @@ _LOGGER = logging.getLogger(__name__) -CONF_NEXT_DEPARTURE = 'nextdeparture' +CONF_NEXT_DEPARTURE = 'next_departure' CONF_STATION = 'station' CONF_DESTINATIONS = 'destinations' CONF_DIRECTIONS = 'directions' CONF_LINES = 'lines' CONF_PRODUCTS = 'products' -CONF_TIMEOFFSET = 'timeoffset' -CONF_MAXJOURNEYS = 'max' +CONF_TIME_OFFSET = 'time_offset' +CONF_MAX_JOURNEYS = 'max' DEFAULT_NAME = 'RMV Journey' @@ -54,13 +54,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_NEXT_DEPARTURE): [{ vol.Required(CONF_STATION): cv.string, - vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list_csv, - vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list_csv, - vol.Optional(CONF_LINES, default=['']): cv.ensure_list_csv, + vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list, + vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list, + vol.Optional(CONF_LINES, default=['']): cv.ensure_list, vol.Optional(CONF_PRODUCTS, default=VALID_PRODUCTS): vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]), - vol.Optional(CONF_TIMEOFFSET, default=0): cv.positive_int, - vol.Optional(CONF_MAXJOURNEYS, default=5): cv.positive_int, + 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}] }) @@ -68,17 +68,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RMV departure sensor.""" sensors = [] - for nextdeparture in config.get(CONF_NEXT_DEPARTURE): + for next_departure in config.get(CONF_NEXT_DEPARTURE): sensors.append( RMVDepartureSensor( - nextdeparture.get(CONF_STATION), - nextdeparture.get(CONF_DESTINATIONS), - nextdeparture.get(CONF_DIRECTIONS), - nextdeparture.get(CONF_LINES), - nextdeparture.get(CONF_PRODUCTS), - nextdeparture.get(CONF_TIMEOFFSET), - nextdeparture.get(CONF_MAXJOURNEYS), - nextdeparture.get(CONF_NAME))) + next_departure[CONF_STATION], + next_departure.get(CONF_DESTINATIONS), + next_departure.get(CONF_DIRECTIONS), + next_departure.get(CONF_LINES), + 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) @@ -86,13 +86,12 @@ class RMVDepartureSensor(Entity): """Implementation of an RMV departure sensor.""" def __init__(self, station, destinations, directions, - lines, products, timeoffset, maxjourneys, name): + lines, products, time_offset, maxjourneys, name): """Initialize the sensor.""" self._station = station self._name = name self.data = RMVDepartureData(station, destinations, directions, - lines, products, timeoffset, maxjourneys) - self._state = STATE_UNKNOWN + lines, products, time_offset, maxjourneys) self._icon = ICONS[None] @property @@ -159,7 +158,7 @@ class RMVDepartureData: """Pull data from the opendata.rmv.de web page.""" def __init__(self, station_id, destinations, directions, - lines, products, timeoffset, maxjourneys): + lines, products, time_offset, maxjourneys): """Initialize the sensor.""" import RMVtransport self.station = None @@ -168,10 +167,10 @@ def __init__(self, station_id, destinations, directions, self._directions = directions self._lines = lines self._products = products - self._timeoffset = timeoffset + self._time_offset = time_offset self._maxjourneys = maxjourneys self.rmv = RMVtransport.RMVtransport() - self.departures = [] + self.departures = {} def update(self): """Update the connection data.""" @@ -199,7 +198,7 @@ def update(self): elif ('' not in self._lines[:1] and journey['number'] not in self._lines): continue - elif journey['minutes'] < self._timeoffset: + elif journey['minutes'] < self._time_offset: continue for k in ['direction', 'departure_time', 'product', 'minutes']: _nextdep[k] = journey.get(k, '') diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py index 693a0cdac6ebe..2db6884ecb74d 100644 --- a/tests/components/sensor/test_rmvtransport.py +++ b/tests/components/sensor/test_rmvtransport.py @@ -8,11 +8,11 @@ from tests.common import get_test_home_assistant VALID_CONFIG_MINIMAL = {'sensor': {'platform': 'rmvtransport', - 'nextdeparture': [{'station': '3000010'}]}} + 'next_departure': [{'station': '3000010'}]}} VALID_CONFIG_NAME = {'sensor': { 'platform': 'rmvtransport', - 'nextdeparture': [ + 'next_departure': [ { 'station': '3000010', 'name': 'My Station', @@ -21,7 +21,7 @@ VALID_CONFIG_MISC = {'sensor': { 'platform': 'rmvtransport', - 'nextdeparture': [ + 'next_departure': [ { 'station': '3000010', 'lines': [21], @@ -32,7 +32,7 @@ VALID_CONFIG_DEST = {'sensor': { 'platform': 'rmvtransport', - 'nextdeparture': [ + 'next_departure': [ { 'station': '3000010', 'destinations': ['Frankfurt (Main) Flughafen Regionalbahnhof', From b7143649caa41eaab0aea1cdbbae26a9ad8ee004 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 21:20:03 +0200 Subject: [PATCH 18/27] Address more code change requests. --- .../components/sensor/rmvtransport.py | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index d786b25c7046f..514b0f25d89ad 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -12,9 +12,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import ( - CONF_NAME, ATTR_ATTRIBUTION, STATE_UNKNOWN - ) +from homeassistant.const import (CONF_NAME, ATTR_ATTRIBUTION) REQUIREMENTS = ['PyRMVtransport==0.0.6'] @@ -28,7 +26,7 @@ CONF_LINES = 'lines' CONF_PRODUCTS = 'products' CONF_TIME_OFFSET = 'time_offset' -CONF_MAX_JOURNEYS = 'max' +CONF_MAX_JOURNEYS = 'max_journeys' DEFAULT_NAME = 'RMV Journey' @@ -86,12 +84,12 @@ class RMVDepartureSensor(Entity): """Implementation of an RMV departure sensor.""" def __init__(self, station, destinations, directions, - lines, products, time_offset, maxjourneys, name): + lines, products, time_offset, max_journeys, name): """Initialize the sensor.""" self._station = station self._name = name - self.data = RMVDepartureData(station, destinations, directions, - lines, products, time_offset, maxjourneys) + self.data = RMVDepartureData(station, destinations, directions, lines, + products, time_offset, max_journeys) self._icon = ICONS[None] @property @@ -102,20 +100,18 @@ def name(self): @property def available(self): """Return True if entity is available.""" - return self._state != STATE_UNKNOWN + return self._state is not None @property def state(self): """Return the next departure time.""" - self._state = self.data.departures[0].get('minutes', None) return self._state @property def state_attributes(self): """Return the state attributes.""" - result = {} try: - result = { + return { 'next_departures': [val for val in self.data.departures[1:]], 'direction': self.data.departures[0].get('direction'), 'line': self.data.departures[0].get('line'), @@ -125,8 +121,7 @@ def state_attributes(self): 'product': self.data.departures[0].get('product'), } except IndexError: - pass - return result + return {} @property def icon(self): @@ -149,7 +144,6 @@ def update(self): self._name = self.data.station self._station = self.data.station self._state = self.data.departures[0].get('minutes') - self._state = self.data.departures[0].get('departure_time') self._icon = ICONS[self.data.departures[0].get('product')] return @@ -158,7 +152,7 @@ class RMVDepartureData: """Pull data from the opendata.rmv.de web page.""" def __init__(self, station_id, destinations, directions, - lines, products, time_offset, maxjourneys): + lines, products, time_offset, max_journeys): """Initialize the sensor.""" import RMVtransport self.station = None @@ -168,7 +162,7 @@ def __init__(self, station_id, destinations, directions, self._lines = lines self._products = products self._time_offset = time_offset - self._maxjourneys = maxjourneys + self._max_journeys = max_journeys self.rmv = RMVtransport.RMVtransport() self.departures = {} @@ -204,6 +198,6 @@ def update(self): _nextdep[k] = journey.get(k, '') _nextdep['line'] = journey.get('number', '') _deps.append(_nextdep) - if len(_deps) > self._maxjourneys: + if len(_deps) > self._max_journeys: break self.departures = _deps From 8be8b078ac637846fbbcda62d7cf05dbe3a82b5e Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 21:29:03 +0200 Subject: [PATCH 19/27] Address even more code change requests. --- homeassistant/components/sensor/rmvtransport.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 514b0f25d89ad..93a84ed764fb4 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -145,7 +145,6 @@ def update(self): self._station = self.data.station self._state = self.data.departures[0].get('minutes') self._icon = ICONS[self.data.departures[0].get('product')] - return class RMVDepartureData: @@ -176,7 +175,7 @@ def update(self): self.departures = {} _LOGGER.warning("Returned data not understood") return - self.station = _data.get('station', None) + self.station = _data.get('station') _deps = [] for journey in _data['journeys']: # find the first departure meeting the criteria From d21cec355b2f15fc4fc4f7f5e2b3eafae5136534 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 21:40:17 +0200 Subject: [PATCH 20/27] Simplify destination check. --- homeassistant/components/sensor/rmvtransport.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 93a84ed764fb4..1ca85aa2c5083 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -52,9 +52,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_NEXT_DEPARTURE): [{ vol.Required(CONF_STATION): cv.string, - vol.Optional(CONF_DESTINATIONS, default=['']): cv.ensure_list, - vol.Optional(CONF_DIRECTIONS, default=['']): cv.ensure_list, - vol.Optional(CONF_LINES, default=['']): cv.ensure_list, + vol.Optional(CONF_DESTINATIONS, default=[]): cv.ensure_list, + vol.Optional(CONF_DIRECTIONS, default=[]): cv.ensure_list, + vol.Optional(CONF_LINES, default=[]): cv.ensure_list, vol.Optional(CONF_PRODUCTS, default=VALID_PRODUCTS): vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]), vol.Optional(CONF_TIME_OFFSET, default=0): cv.positive_int, @@ -180,7 +180,7 @@ def update(self): for journey in _data['journeys']: # find the first departure meeting the criteria _nextdep = {ATTR_ATTRIBUTION: ATTRIBUTION} - if '' not in self._destinations[:1]: + if self._destinations: dest_found = False for dest in self._destinations: if dest in journey['stops']: @@ -188,8 +188,7 @@ def update(self): _nextdep['destination'] = dest if not dest_found: continue - elif ('' not in self._lines[:1] and - journey['number'] not in self._lines): + elif self._lines and journey['number'] not in self._lines: continue elif journey['minutes'] < self._time_offset: continue From 4341b8a480df5ab9efb07f5e89c3bcba1e60c7eb Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 22:30:35 +0200 Subject: [PATCH 21/27] Fix linter problem. --- homeassistant/components/sensor/rmvtransport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 1ca85aa2c5083..3f21180a528b8 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -88,6 +88,7 @@ def __init__(self, station, destinations, directions, """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._icon = ICONS[None] From 72c3b7ef5f12ee5a44230693c37d6fbf787228e1 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 9 Aug 2018 23:53:49 +0200 Subject: [PATCH 22/27] Bump dependency version to 0.0.7. --- homeassistant/components/sensor/rmvtransport.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 3f21180a528b8..8beafaa002d02 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (CONF_NAME, ATTR_ATTRIBUTION) -REQUIREMENTS = ['PyRMVtransport==0.0.6'] +REQUIREMENTS = ['PyRMVtransport==0.0.7'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 45fd8193b0e66..5f304cbf4268d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -48,7 +48,7 @@ PyMVGLive==1.1.4 PyMata==2.14 # homeassistant.components.sensor.rmvtransport -PyRMVtransport==0.0.6 +PyRMVtransport==0.0.7 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8c842ecf15705..81fc5630847dc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -25,7 +25,7 @@ HAP-python==2.2.2 PyJWT==1.6.0 # homeassistant.components.sensor.rmvtransport -PyRMVtransport==0.0.6 +PyRMVtransport==0.0.7 # homeassistant.components.sonos SoCo==0.14 From 9f29d304e7c0d80c87d0ef0da1a04225ad21c093 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 10 Aug 2018 13:53:40 +0200 Subject: [PATCH 23/27] Name variable more explicit. --- homeassistant/components/sensor/rmvtransport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 8beafaa002d02..3fe3b530a5b6c 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -193,8 +193,8 @@ def update(self): continue elif journey['minutes'] < self._time_offset: continue - for k in ['direction', 'departure_time', 'product', 'minutes']: - _nextdep[k] = journey.get(k, '') + for attr in ['direction', 'departure_time', 'product', 'minutes']: + _nextdep[attr] = journey.get(attr, '') _nextdep['line'] = journey.get('number', '') _deps.append(_nextdep) if len(_deps) > self._max_journeys: From 844c4396af6fd04dc28bcf70274a3c8113b9a343 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 10 Aug 2018 14:05:53 +0200 Subject: [PATCH 24/27] Only query once a minute. --- homeassistant/components/sensor/rmvtransport.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 3fe3b530a5b6c..c6ea9df87d86f 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -47,8 +47,6 @@ } ATTRIBUTION = "Data provided by opendata.rmv.de" -SCAN_INTERVAL = timedelta(seconds=30) - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_NEXT_DEPARTURE): [{ vol.Required(CONF_STATION): cv.string, @@ -164,7 +162,7 @@ def __init__(self, station_id, destinations, directions, self._time_offset = time_offset self._max_journeys = max_journeys self.rmv = RMVtransport.RMVtransport() - self.departures = {} + self.departures = [] def update(self): """Update the connection data.""" @@ -173,7 +171,7 @@ def update(self): products=self._products, maxJourneys=50) except ValueError: - self.departures = {} + self.departures = [] _LOGGER.warning("Returned data not understood") return self.station = _data.get('station') From cacfd467102bfc0a498ed58c5a3eecfa1e3c1aed Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 10 Aug 2018 14:07:19 +0200 Subject: [PATCH 25/27] Update test case. --- tests/components/sensor/test_rmvtransport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py index 2db6884ecb74d..56ef46fb7a1aa 100644 --- a/tests/components/sensor/test_rmvtransport.py +++ b/tests/components/sensor/test_rmvtransport.py @@ -25,8 +25,8 @@ { 'station': '3000010', 'lines': [21], - 'max': 2, - 'timeoffset': 10 + 'max_journeys': 2, + 'time_offset': 10 } ]}} From a9949a432fb0032a43963739e716524daaddbaf2 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 10 Aug 2018 14:23:18 +0200 Subject: [PATCH 26/27] Fix config validation. --- homeassistant/components/sensor/rmvtransport.py | 9 ++++++--- tests/components/sensor/test_rmvtransport.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index c6ea9df87d86f..20e8027e74198 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -50,9 +50,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_NEXT_DEPARTURE): [{ vol.Required(CONF_STATION): cv.string, - vol.Optional(CONF_DESTINATIONS, default=[]): cv.ensure_list, - vol.Optional(CONF_DIRECTIONS, default=[]): cv.ensure_list, - vol.Optional(CONF_LINES, default=[]): cv.ensure_list, + vol.Optional(CONF_DESTINATIONS, default=[]): + vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_DIRECTIONS, default=[]): + vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_LINES, default=[]): + vol.All(cv.ensure_list, [cv.positive_int, cv.string]), vol.Optional(CONF_PRODUCTS, default=VALID_PRODUCTS): vol.All(cv.ensure_list, [vol.In(VALID_PRODUCTS)]), vol.Optional(CONF_TIME_OFFSET, default=0): cv.positive_int, diff --git a/tests/components/sensor/test_rmvtransport.py b/tests/components/sensor/test_rmvtransport.py index 56ef46fb7a1aa..9db19ecde499e 100644 --- a/tests/components/sensor/test_rmvtransport.py +++ b/tests/components/sensor/test_rmvtransport.py @@ -24,7 +24,7 @@ 'next_departure': [ { 'station': '3000010', - 'lines': [21], + 'lines': [21, 'S8'], 'max_journeys': 2, 'time_offset': 10 } From 1642b47d582eda96d08b664b3e1fb454989883be Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Fri, 10 Aug 2018 14:28:12 +0200 Subject: [PATCH 27/27] Remove unneeded import. --- homeassistant/components/sensor/rmvtransport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/rmvtransport.py b/homeassistant/components/sensor/rmvtransport.py index 20e8027e74198..3d7fd2aa3b70a 100644 --- a/homeassistant/components/sensor/rmvtransport.py +++ b/homeassistant/components/sensor/rmvtransport.py @@ -5,7 +5,6 @@ https://home-assistant.io/components/sensor.rmvtransport/ """ import logging -from datetime import timedelta import voluptuous as vol