From ba0f7a41010da7ce4372c3631b2753fb64108caa Mon Sep 17 00:00:00 2001 From: Thibault Cohen Date: Fri, 29 Dec 2017 12:33:11 -0500 Subject: [PATCH] Fido component use now asyncio (#11244) * Fido component use now asyncio * Fix comments * Fix comments 2 * Fix assertion for test error * Update to pyfido 2.1.0 --- .coveragerc | 1 - homeassistant/components/sensor/fido.py | 42 ++++----- requirements_all.txt | 2 +- tests/components/sensor/test_fido.py | 109 ++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 tests/components/sensor/test_fido.py diff --git a/.coveragerc b/.coveragerc index 6d2997a8274..dede973976e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -520,7 +520,6 @@ omit = homeassistant/components/sensor/etherscan.py homeassistant/components/sensor/fastdotcom.py homeassistant/components/sensor/fedex.py - homeassistant/components/sensor/fido.py homeassistant/components/sensor/fitbit.py homeassistant/components/sensor/fixer.py homeassistant/components/sensor/fritzbox_callmonitor.py diff --git a/homeassistant/components/sensor/fido.py b/homeassistant/components/sensor/fido.py index c4f4217616f..07c085cd18d 100644 --- a/homeassistant/components/sensor/fido.py +++ b/homeassistant/components/sensor/fido.py @@ -7,10 +7,10 @@ https://www.fido.ca/pages/#/my-account/wireless For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.fido/ """ +import asyncio import logging from datetime import timedelta -import requests import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA @@ -21,7 +21,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyfido==1.0.1'] +REQUIREMENTS = ['pyfido==2.1.0'] _LOGGER = logging.getLogger(__name__) @@ -70,17 +70,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up the Fido sensor.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) - try: - fido_data = FidoData(username, password) - fido_data.update() - except requests.exceptions.HTTPError as error: - _LOGGER.error("Failt login: %s", error) - return False + httpsession = hass.helpers.aiohttp_client.async_get_clientsession() + fido_data = FidoData(username, password, httpsession) + ret = yield from fido_data.async_update() + if ret is False: + return name = config.get(CONF_NAME) @@ -89,7 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(FidoSensor(fido_data, variable, name, number)) - add_devices(sensors, True) + async_add_devices(sensors, True) class FidoSensor(Entity): @@ -133,9 +133,10 @@ class FidoSensor(Entity): 'number': self._number, } - def update(self): + @asyncio.coroutine + def async_update(self): """Get the latest data from Fido and update the state.""" - self.fido_data.update() + yield from self.fido_data.async_update() if self.type == 'balance': if self.fido_data.data.get(self.type) is not None: self._state = round(self.fido_data.data[self.type], 2) @@ -149,20 +150,23 @@ class FidoSensor(Entity): class FidoData(object): """Get data from Fido.""" - def __init__(self, username, password): + def __init__(self, username, password, httpsession): """Initialize the data object.""" from pyfido import FidoClient - self.client = FidoClient(username, password, REQUESTS_TIMEOUT) + self.client = FidoClient(username, password, + REQUESTS_TIMEOUT, httpsession) self.data = {} + @asyncio.coroutine @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): + def async_update(self): """Get the latest data from Fido.""" from pyfido.client import PyFidoError try: - self.client.fetch_data() - except PyFidoError as err: - _LOGGER.error("Error on receive last Fido data: %s", err) - return + yield from self.client.fetch_data() + except PyFidoError as exp: + _LOGGER.error("Error on receive last Fido data: %s", exp) + return False # Update data self.data = self.client.get_data() + return True diff --git a/requirements_all.txt b/requirements_all.txt index 78a8e6537b9..c1e7bccdd3f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -686,7 +686,7 @@ pyenvisalink==2.2 pyephember==0.1.1 # homeassistant.components.sensor.fido -pyfido==1.0.1 +pyfido==2.1.0 # homeassistant.components.climate.flexit pyflexit==0.3 diff --git a/tests/components/sensor/test_fido.py b/tests/components/sensor/test_fido.py new file mode 100644 index 00000000000..1eca7be7544 --- /dev/null +++ b/tests/components/sensor/test_fido.py @@ -0,0 +1,109 @@ +"""The test for the fido sensor platform.""" +import asyncio +import logging +import sys +from unittest.mock import MagicMock + +from homeassistant.bootstrap import async_setup_component +from homeassistant.components.sensor import fido +from tests.common import assert_setup_component + + +CONTRACT = "123456789" + + +class FidoClientMock(): + """Fake Fido client.""" + + def __init__(self, username, password, timeout=None, httpsession=None): + """Fake Fido client init.""" + pass + + def get_phone_numbers(self): + """Return Phone numbers.""" + return ["1112223344"] + + def get_data(self): + """Return fake fido data.""" + return {"balance": 160.12, + "1112223344": {"data_remaining": 100.33}} + + @asyncio.coroutine + def fetch_data(self): + """Return fake fetching data.""" + pass + + +class FidoClientMockError(FidoClientMock): + """Fake Fido client error.""" + + @asyncio.coroutine + def fetch_data(self): + """Return fake fetching data.""" + raise PyFidoErrorMock("Fake Error") + + +class PyFidoErrorMock(Exception): + """Fake PyFido Error.""" + + +class PyFidoClientFakeModule(): + """Fake pyfido.client module.""" + + PyFidoError = PyFidoErrorMock + + +class PyFidoFakeModule(): + """Fake pyfido module.""" + + FidoClient = FidoClientMockError + + +def fake_async_add_devices(component, update_before_add=False): + """Fake async_add_devices function.""" + pass + + +@asyncio.coroutine +def test_fido_sensor(loop, hass): + """Test the Fido number sensor.""" + sys.modules['pyfido'] = MagicMock() + sys.modules['pyfido.client'] = MagicMock() + sys.modules['pyfido.client.PyFidoError'] = \ + PyFidoErrorMock + import pyfido.client + pyfido.FidoClient = FidoClientMock + pyfido.client.PyFidoError = PyFidoErrorMock + config = { + 'sensor': { + 'platform': 'fido', + 'name': 'fido', + 'username': 'myusername', + 'password': 'password', + 'monitored_variables': [ + 'balance', + 'data_remaining', + ], + } + } + with assert_setup_component(1): + yield from async_setup_component(hass, 'sensor', config) + state = hass.states.get('sensor.fido_1112223344_balance') + assert state.state == "160.12" + assert state.attributes.get('number') == "1112223344" + state = hass.states.get('sensor.fido_1112223344_data_remaining') + assert state.state == "100.33" + + +@asyncio.coroutine +def test_error(hass, caplog): + """Test the Fido sensor errors.""" + caplog.set_level(logging.ERROR) + sys.modules['pyfido'] = PyFidoFakeModule() + sys.modules['pyfido.client'] = PyFidoClientFakeModule() + + config = {} + fake_async_add_devices = MagicMock() + yield from fido.async_setup_platform(hass, config, + fake_async_add_devices) + assert fake_async_add_devices.called is False