From a08f97add674fac13c85f2f718303ac09310c4ca Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 23 Jan 2019 01:20:49 -0800 Subject: [PATCH 1/6] Split out fastdotcom into a component and a sensor platform --- .../components/fastdotcom/__init__.py | 83 +++++++++++++ homeassistant/components/fastdotcom/sensor.py | 71 +++++++++++ .../components/fastdotcom/services.yaml | 2 + homeassistant/components/sensor/fastdotcom.py | 115 ------------------ requirements_all.txt | 2 +- 5 files changed, 157 insertions(+), 116 deletions(-) create mode 100644 homeassistant/components/fastdotcom/__init__.py create mode 100644 homeassistant/components/fastdotcom/sensor.py create mode 100644 homeassistant/components/fastdotcom/services.yaml delete mode 100644 homeassistant/components/sensor/fastdotcom.py diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py new file mode 100644 index 00000000000000..5ba7461e4c4f5f --- /dev/null +++ b/homeassistant/components/fastdotcom/__init__.py @@ -0,0 +1,83 @@ +""" +Support for testing internet speed via Fast.com. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/fastdotcom/ +""" + +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.discovery import async_load_platform +from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import track_time_change + +REQUIREMENTS = ['fastdotcom==0.0.3'] + +DOMAIN = 'fastdotcom' +DATA_UPDATED = '{}_data_updated'.format(DOMAIN) + +_LOGGER = logging.getLogger(__name__) + +CONF_SECOND = 'second' +CONF_MINUTE = 'minute' +CONF_HOUR = 'hour' +CONF_MANUAL = 'manual' + +# pylint: disable=invalid-name +minutes_or_seconds = vol.All(vol.Coerce(int), vol.Range(0, 59)) +hours = vol.All(vol.Coerce(int), vol.Range(0, 23)) +# pylint: enable=invalid-name + + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_SECOND, default=[0]): + vol.All(cv.ensure_list, [minutes_or_seconds]), + vol.Optional(CONF_MINUTE, default=[0]): + vol.All(cv.ensure_list, [minutes_or_seconds]), + vol.Optional(CONF_HOUR): + vol.All(cv.ensure_list, [hours]), + vol.Optional(CONF_MANUAL, default=False): cv.boolean, + }) +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the Fast.com component.""" + conf = config[DOMAIN] + data = hass.data[DOMAIN] = SpeedtestData(hass, conf) + + def update(call=None): + """Service call to manually update the data.""" + data.update() + + hass.services.register(DOMAIN, 'speedtest', update) + + hass.async_create_task( + async_load_platform(hass, 'sensor', DOMAIN, {}, config) + ) + + return True + + +class SpeedtestData: + """Get the latest data from fast.com.""" + + def __init__(self, hass, config): + """Initialize the data object.""" + self.data = None + self._hass = hass + if not config.get(CONF_MANUAL): + track_time_change( + hass, self.update, second=config.get(CONF_SECOND), + minute=config.get(CONF_MINUTE), hour=config.get(CONF_HOUR)) + + def update(self): + """Get the latest data from fast.com.""" + from fastdotcom import fast_com + _LOGGER.info("Executing fast.com speedtest") + self.data = {'download': fast_com()} + dispatcher_send(self._hass, DATA_UPDATED) diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py new file mode 100644 index 00000000000000..cd3f55f55180ca --- /dev/null +++ b/homeassistant/components/fastdotcom/sensor.py @@ -0,0 +1,71 @@ +""" +Support for Fast.com internet speed testing sensor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.fastdotcom/ +""" +import logging + +from homeassistant.components.fastdotcom import DOMAIN as FASTDOTCOM_DOMAIN, \ + DATA_UPDATED +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.restore_state import RestoreEntity + +DEPENDENCIES = ['fastdotcom'] + +_LOGGER = logging.getLogger(__name__) + +ICON = 'mdi:speedometer' + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Fast.com sensor.""" + add_entities([SpeedtestSensor(hass.data[FASTDOTCOM_DOMAIN])]) + + +class SpeedtestSensor(RestoreEntity): + """Implementation of a FAst.com sensor.""" + + def __init__(self, speedtest_data): + """Initialize the sensor.""" + self._name = 'Fast.com Download' + self.speedtest_client = speedtest_data + self._state = None + self._unit_of_measurement = 'Mbit/s' + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self._unit_of_measurement + + def update(self): + """Get the latest data and update the states.""" + data = self.speedtest_client.data + if data is None: + return + self._state = data['download'] + + async def async_added_to_hass(self): + """Handle entity which will be added.""" + await super().async_added_to_hass() + state = await self.async_get_last_state() + if not state: + return + self._state = state.state + + async_dispatcher_connect(self.hass, DATA_UPDATED, self.update) + + @property + def icon(self): + """Return icon.""" + return ICON diff --git a/homeassistant/components/fastdotcom/services.yaml b/homeassistant/components/fastdotcom/services.yaml new file mode 100644 index 00000000000000..fe6cb1ac12dbab --- /dev/null +++ b/homeassistant/components/fastdotcom/services.yaml @@ -0,0 +1,2 @@ +speedtest: + description: Immediately take a speedest with Fast.com \ No newline at end of file diff --git a/homeassistant/components/sensor/fastdotcom.py b/homeassistant/components/sensor/fastdotcom.py deleted file mode 100644 index 8e975c48574239..00000000000000 --- a/homeassistant/components/sensor/fastdotcom.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -Support for Fast.com internet speed testing sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fastdotcom/ -""" -import logging - -import voluptuous as vol - -from homeassistant.components.sensor import DOMAIN, PLATFORM_SCHEMA -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.event import track_time_change -from homeassistant.helpers.restore_state import RestoreEntity -import homeassistant.util.dt as dt_util - -REQUIREMENTS = ['fastdotcom==0.0.3'] - -_LOGGER = logging.getLogger(__name__) - -CONF_SECOND = 'second' -CONF_MINUTE = 'minute' -CONF_HOUR = 'hour' -CONF_MANUAL = 'manual' - -ICON = 'mdi:speedometer' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_SECOND, default=[0]): - vol.All(cv.ensure_list, [vol.All(vol.Coerce(int), vol.Range(0, 59))]), - vol.Optional(CONF_MINUTE, default=[0]): - vol.All(cv.ensure_list, [vol.All(vol.Coerce(int), vol.Range(0, 59))]), - vol.Optional(CONF_HOUR): - vol.All(cv.ensure_list, [vol.All(vol.Coerce(int), vol.Range(0, 23))]), - vol.Optional(CONF_MANUAL, default=False): cv.boolean, -}) - - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the Fast.com sensor.""" - data = SpeedtestData(hass, config) - sensor = SpeedtestSensor(data) - add_entities([sensor]) - - def update(call=None): - """Update service for manual updates.""" - data.update(dt_util.now()) - sensor.update() - - hass.services.register(DOMAIN, 'update_fastdotcom', update) - - -class SpeedtestSensor(RestoreEntity): - """Implementation of a FAst.com sensor.""" - - def __init__(self, speedtest_data): - """Initialize the sensor.""" - self._name = 'Fast.com Download' - self.speedtest_client = speedtest_data - self._state = None - self._unit_of_measurement = 'Mbit/s' - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement - - def update(self): - """Get the latest data and update the states.""" - data = self.speedtest_client.data - if data is None: - return - - self._state = data['download'] - - async def async_added_to_hass(self): - """Handle entity which will be added.""" - await super().async_added_to_hass() - state = await self.async_get_last_state() - if not state: - return - self._state = state.state - - @property - def icon(self): - """Return icon.""" - return ICON - - -class SpeedtestData: - """Get the latest data from fast.com.""" - - def __init__(self, hass, config): - """Initialize the data object.""" - self.data = None - if not config.get(CONF_MANUAL): - track_time_change( - hass, self.update, second=config.get(CONF_SECOND), - minute=config.get(CONF_MINUTE), hour=config.get(CONF_HOUR)) - - def update(self, now): - """Get the latest data from fast.com.""" - from fastdotcom import fast_com - _LOGGER.info("Executing fast.com speedtest") - self.data = {'download': fast_com()} diff --git a/requirements_all.txt b/requirements_all.txt index 8908e251afc972..bd7eb767084ec3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -398,7 +398,7 @@ evohomeclient==0.2.8 # homeassistant.components.image_processing.dlib_face_identify # face_recognition==1.0.0 -# homeassistant.components.sensor.fastdotcom +# homeassistant.components.fastdotcom fastdotcom==0.0.3 # homeassistant.components.sensor.fedex From 057f57835b34c672cb41bc5ae486bb7259d662e4 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 23 Jan 2019 01:26:16 -0800 Subject: [PATCH 2/6] Update .coveragerc --- .coveragerc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 722f74a0b6a049..9e7d126230e7be 100644 --- a/.coveragerc +++ b/.coveragerc @@ -142,6 +142,8 @@ omit = homeassistant/components/eufy.py homeassistant/components/*/eufy.py + homeassistant/components/fastdotcom/* + homeassistant/components/fibaro/__init__.py homeassistant/components/*/fibaro.py @@ -778,7 +780,6 @@ omit = homeassistant/components/sensor/enphase_envoy.py homeassistant/components/sensor/envirophat.py homeassistant/components/sensor/etherscan.py - homeassistant/components/sensor/fastdotcom.py homeassistant/components/sensor/fedex.py homeassistant/components/sensor/filesize.py homeassistant/components/sensor/fints.py From 32851f514bc26c95fa1538c98a461d292ebf5675 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Sun, 27 Jan 2019 18:04:58 -0800 Subject: [PATCH 3/6] Switching to async and using a Throttle --- .../components/fastdotcom/__init__.py | 47 +++++++++---------- homeassistant/components/fastdotcom/sensor.py | 19 ++++++-- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py index 5ba7461e4c4f5f..ebf30875da85da 100644 --- a/homeassistant/components/fastdotcom/__init__.py +++ b/homeassistant/components/fastdotcom/__init__.py @@ -6,13 +6,15 @@ """ import logging +from datetime import timedelta import voluptuous as vol import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_UPDATE_INTERVAL from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import dispatcher_send -from homeassistant.helpers.event import track_time_change +from homeassistant.util import Throttle REQUIREMENTS = ['fastdotcom==0.0.3'] @@ -21,40 +23,33 @@ _LOGGER = logging.getLogger(__name__) -CONF_SECOND = 'second' -CONF_MINUTE = 'minute' -CONF_HOUR = 'hour' CONF_MANUAL = 'manual' -# pylint: disable=invalid-name -minutes_or_seconds = vol.All(vol.Coerce(int), vol.Range(0, 59)) -hours = vol.All(vol.Coerce(int), vol.Range(0, 23)) -# pylint: enable=invalid-name - +DEFAULT_INTERVAL = timedelta(hours=1) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Optional(CONF_SECOND, default=[0]): - vol.All(cv.ensure_list, [minutes_or_seconds]), - vol.Optional(CONF_MINUTE, default=[0]): - vol.All(cv.ensure_list, [minutes_or_seconds]), - vol.Optional(CONF_HOUR): - vol.All(cv.ensure_list, [hours]), + vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_INTERVAL): + vol.All( + cv.time_period, cv.positive_timedelta + ), vol.Optional(CONF_MANUAL, default=False): cv.boolean, }) }, extra=vol.ALLOW_EXTRA) -def setup(hass, config): +async def async_setup(hass, config): """Set up the Fast.com component.""" conf = config[DOMAIN] - data = hass.data[DOMAIN] = SpeedtestData(hass, conf) + data = hass.data[DOMAIN] = SpeedtestData( + hass, conf[CONF_UPDATE_INTERVAL], conf[CONF_MANUAL] + ) def update(call=None): """Service call to manually update the data.""" - data.update() + data.update(no_throttle=True) - hass.services.register(DOMAIN, 'speedtest', update) + hass.services.async_register(DOMAIN, 'speedtest', update) hass.async_create_task( async_load_platform(hass, 'sensor', DOMAIN, {}, config) @@ -66,18 +61,18 @@ def update(call=None): class SpeedtestData: """Get the latest data from fast.com.""" - def __init__(self, hass, config): + def __init__(self, hass, interval, manual): """Initialize the data object.""" self.data = None self._hass = hass - if not config.get(CONF_MANUAL): - track_time_change( - hass, self.update, second=config.get(CONF_SECOND), - minute=config.get(CONF_MINUTE), hour=config.get(CONF_HOUR)) + if not manual: + self.update = Throttle(interval)(self._update) + else: + self.update = self._update - def update(self): + def _update(self): """Get the latest data from fast.com.""" from fastdotcom import fast_com - _LOGGER.info("Executing fast.com speedtest") + _LOGGER.debug("Executing fast.com speedtest") self.data = {'download': fast_com()} dispatcher_send(self._hass, DATA_UPDATED) diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py index cd3f55f55180ca..3dcd4b5129cc3b 100644 --- a/homeassistant/components/fastdotcom/sensor.py +++ b/homeassistant/components/fastdotcom/sensor.py @@ -8,6 +8,7 @@ from homeassistant.components.fastdotcom import DOMAIN as FASTDOTCOM_DOMAIN, \ DATA_UPDATED +from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity @@ -17,10 +18,13 @@ ICON = 'mdi:speedometer' +UNIT_OF_MEASUREMENT = 'Mbit/s' -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 Fast.com sensor.""" - add_entities([SpeedtestSensor(hass.data[FASTDOTCOM_DOMAIN])]) + async_add_entities([SpeedtestSensor(hass.data[FASTDOTCOM_DOMAIN])]) class SpeedtestSensor(RestoreEntity): @@ -31,7 +35,6 @@ def __init__(self, speedtest_data): self._name = 'Fast.com Download' self.speedtest_client = speedtest_data self._state = None - self._unit_of_measurement = 'Mbit/s' @property def name(self): @@ -46,7 +49,7 @@ def state(self): @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement + return UNIT_OF_MEASUREMENT def update(self): """Get the latest data and update the states.""" @@ -55,6 +58,10 @@ def update(self): return self._state = data['download'] + @callback + def _schedule_immediate_update(self): + self.async_schedule_update_ha_state(True) + async def async_added_to_hass(self): """Handle entity which will be added.""" await super().async_added_to_hass() @@ -63,7 +70,9 @@ async def async_added_to_hass(self): return self._state = state.state - async_dispatcher_connect(self.hass, DATA_UPDATED, self.update) + async_dispatcher_connect( + self.hass, DATA_UPDATED, self._schedule_immediate_update + ) @property def icon(self): From e23c1a77abe82f33f07308a2ca0bbef43dcefced Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Sun, 27 Jan 2019 23:57:57 -0800 Subject: [PATCH 4/6] Add the async_track_time_interval call --- homeassistant/components/fastdotcom/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py index ebf30875da85da..9600fbfaa40bfa 100644 --- a/homeassistant/components/fastdotcom/__init__.py +++ b/homeassistant/components/fastdotcom/__init__.py @@ -9,6 +9,7 @@ from datetime import timedelta import voluptuous as vol +from homeassistant.helpers.event import async_track_time_interval import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_UPDATE_INTERVAL @@ -67,6 +68,7 @@ def __init__(self, hass, interval, manual): self._hass = hass if not manual: self.update = Throttle(interval)(self._update) + async_track_time_interval(self._hass, self.update, interval) else: self.update = self._update From afda0b0e40a73429c383cc851e25d8d895d4ae1c Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Mon, 28 Jan 2019 00:01:28 -0800 Subject: [PATCH 5/6] Remove the throttle --- homeassistant/components/fastdotcom/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py index 9600fbfaa40bfa..b5df45216e5533 100644 --- a/homeassistant/components/fastdotcom/__init__.py +++ b/homeassistant/components/fastdotcom/__init__.py @@ -9,13 +9,12 @@ from datetime import timedelta import voluptuous as vol -from homeassistant.helpers.event import async_track_time_interval import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_UPDATE_INTERVAL from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import dispatcher_send -from homeassistant.util import Throttle +from homeassistant.helpers.event import async_track_time_interval REQUIREMENTS = ['fastdotcom==0.0.3'] @@ -48,7 +47,7 @@ async def async_setup(hass, config): def update(call=None): """Service call to manually update the data.""" - data.update(no_throttle=True) + data.update() hass.services.async_register(DOMAIN, 'speedtest', update) @@ -67,12 +66,9 @@ def __init__(self, hass, interval, manual): self.data = None self._hass = hass if not manual: - self.update = Throttle(interval)(self._update) async_track_time_interval(self._hass, self.update, interval) - else: - self.update = self._update - def _update(self): + def update(self): """Get the latest data from fast.com.""" from fastdotcom import fast_com _LOGGER.debug("Executing fast.com speedtest") From 05f8421cbe50c98064dbb59c3b1f0c02eea3c4bf Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Mon, 28 Jan 2019 00:35:43 -0800 Subject: [PATCH 6/6] Reorder sensor methods and add should_poll property --- homeassistant/components/fastdotcom/sensor.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py index 3dcd4b5129cc3b..4d105cebf449f3 100644 --- a/homeassistant/components/fastdotcom/sensor.py +++ b/homeassistant/components/fastdotcom/sensor.py @@ -51,16 +51,15 @@ def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return UNIT_OF_MEASUREMENT - def update(self): - """Get the latest data and update the states.""" - data = self.speedtest_client.data - if data is None: - return - self._state = data['download'] + @property + def icon(self): + """Return icon.""" + return ICON - @callback - def _schedule_immediate_update(self): - self.async_schedule_update_ha_state(True) + @property + def should_poll(self): + """Return the polling requirement for this sensor.""" + return False async def async_added_to_hass(self): """Handle entity which will be added.""" @@ -74,7 +73,13 @@ async def async_added_to_hass(self): self.hass, DATA_UPDATED, self._schedule_immediate_update ) - @property - def icon(self): - """Return icon.""" - return ICON + def update(self): + """Get the latest data and update the states.""" + data = self.speedtest_client.data + if data is None: + return + self._state = data['download'] + + @callback + def _schedule_immediate_update(self): + self.async_schedule_update_ha_state(True)