From 8bdc824b6cfb9ae69ee78b7cd46d2f18383acbeb Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 13 Oct 2020 08:48:26 +0200 Subject: [PATCH] Update pypoint to use async http requests (#41546) * Remove domain after entities * Add support for async http-requests * Fix, handle network issues (update _is_available status) * Fix missing await for alarm_arm * Fix alarm status * Update tests to async * Bump pypoint version * Fix doc string * Apply suggestions from code review, remove pylint disable * Fix black --- homeassistant/components/point/__init__.py | 45 +++++++++---------- .../components/point/alarm_control_panel.py | 8 ++-- homeassistant/components/point/config_flow.py | 18 +++++--- homeassistant/components/point/manifest.json | 2 +- homeassistant/components/point/sensor.py | 8 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/point/test_config_flow.py | 10 +++-- 8 files changed, 49 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/point/__init__.py b/homeassistant/components/point/__init__.py index ba6c621dbd6..13dce13c0da 100644 --- a/homeassistant/components/point/__init__.py +++ b/homeassistant/components/point/__init__.py @@ -75,22 +75,22 @@ async def async_setup(hass, config): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Set up Point from a config entry.""" - def token_saver(token): - _LOGGER.debug("Saving updated token") + async def token_saver(token, **kwargs): + _LOGGER.debug("Saving updated token %s", token) hass.config_entries.async_update_entry( entry, data={**entry.data, CONF_TOKEN: token} ) - # Force token update. - entry.data[CONF_TOKEN]["expires_in"] = -1 session = PointSession( + hass.helpers.aiohttp_client.async_get_clientsession(), entry.data["refresh_args"][CONF_CLIENT_ID], + entry.data["refresh_args"][CONF_CLIENT_SECRET], token=entry.data[CONF_TOKEN], - auto_refresh_kwargs=entry.data["refresh_args"], token_saver=token_saver, ) - - if not session.is_authorized: + try: + await session.ensure_active_token() + except Exception: # pylint: disable=broad-except _LOGGER.error("Authentication Error") return False @@ -120,8 +120,7 @@ async def async_setup_webhook(hass: HomeAssistantType, entry: ConfigEntry, sessi CONF_WEBHOOK_URL: webhook_url, }, ) - await hass.async_add_executor_job( - session.update_webhook, + await session.update_webhook( entry.data[CONF_WEBHOOK_URL], entry.data[CONF_WEBHOOK_ID], ["*"], @@ -136,14 +135,14 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry): """Unload a config entry.""" hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) session = hass.data[DOMAIN].pop(entry.entry_id) - await hass.async_add_executor_job(session.remove_webhook) - - if not hass.data[DOMAIN]: - hass.data.pop(DOMAIN) + await session.remove_webhook() for component in ("binary_sensor", "sensor"): await hass.config_entries.async_forward_entry_unload(entry, component) + if not hass.data[DOMAIN]: + hass.data.pop(DOMAIN) + return True @@ -181,12 +180,10 @@ class MinutPointClient: async def _sync(self): """Update local list of devices.""" - if ( - not await self._hass.async_add_executor_job(self._client.update) - and self._is_available - ): + if not await self._client.update() and self._is_available: self._is_available = False _LOGGER.warning("Device is unavailable") + async_dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY) return async def new_device(device_id, component): @@ -221,24 +218,26 @@ class MinutPointClient: def is_available(self, device_id): """Return device availability.""" + if not self._is_available: + return False return device_id in self._client.device_ids - def remove_webhook(self): + async def remove_webhook(self): """Remove the session webhook.""" - return self._client.remove_webhook() + return await self._client.remove_webhook() @property def homes(self): """Return known homes.""" return self._client.homes - def alarm_disarm(self, home_id): + async def async_alarm_disarm(self, home_id): """Send alarm disarm command.""" - return self._client.alarm_disarm(home_id) + return await self._client.alarm_disarm(home_id) - def alarm_arm(self, home_id): + async def async_alarm_arm(self, home_id): """Send alarm arm command.""" - return self._client.alarm_arm(home_id) + return await self._client.alarm_arm(home_id) class MinutPointEntity(Entity): diff --git a/homeassistant/components/point/alarm_control_panel.py b/homeassistant/components/point/alarm_control_panel.py index 25e135f59cb..8325c89f129 100644 --- a/homeassistant/components/point/alarm_control_panel.py +++ b/homeassistant/components/point/alarm_control_panel.py @@ -99,15 +99,15 @@ class MinutPointAlarmControl(AlarmControlPanelEntity): """Return the user the last change was triggered by.""" return self._changed_by - def alarm_disarm(self, code=None): + async def async_alarm_disarm(self, code=None): """Send disarm command.""" - status = self._client.alarm_disarm(self._home_id) + status = await self._client.async_alarm_disarm(self._home_id) if status: self._home["alarm_status"] = "off" - def alarm_arm_away(self, code=None): + async def async_alarm_arm_away(self, code=None): """Send arm away command.""" - status = self._client.alarm_arm(self._home_id) + status = await self._client.async_alarm_arm(self._home_id) if status: self._home["alarm_status"] = "on" diff --git a/homeassistant/components/point/config_flow.py b/homeassistant/components/point/config_flow.py index 5a343f276a7..815b872d3e9 100644 --- a/homeassistant/components/point/config_flow.py +++ b/homeassistant/components/point/config_flow.py @@ -102,7 +102,6 @@ class PointFlowHandler(config_entries.ConfigFlow): except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected error generating auth url") return self.async_abort(reason="authorize_url_fail") - return self.async_show_form( step_id="auth", description_placeholders={"authorization_url": url}, @@ -111,11 +110,14 @@ class PointFlowHandler(config_entries.ConfigFlow): async def _get_authorization_url(self): """Create Minut Point session and get authorization url.""" - flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl] client_id = flow[CONF_CLIENT_ID] client_secret = flow[CONF_CLIENT_SECRET] - point_session = PointSession(client_id, client_secret=client_secret) + point_session = PointSession( + self.hass.helpers.aiohttp_client.async_get_clientsession(), + client_id, + client_secret, + ) self.hass.http.register_view(MinutAuthCallbackView()) @@ -143,17 +145,19 @@ class PointFlowHandler(config_entries.ConfigFlow): flow = self.hass.data[DATA_FLOW_IMPL][DOMAIN] client_id = flow[CONF_CLIENT_ID] client_secret = flow[CONF_CLIENT_SECRET] - point_session = PointSession(client_id, client_secret=client_secret) - token = await self.hass.async_add_executor_job( - point_session.get_access_token, code + point_session = PointSession( + self.hass.helpers.aiohttp_client.async_get_clientsession(), + client_id, + client_secret, ) + token = await point_session.get_access_token(code) _LOGGER.debug("Got new token") if not point_session.is_authorized: _LOGGER.error("Authentication Error") return self.async_abort(reason="auth_error") _LOGGER.info("Successfully authenticated Point") - user_email = point_session.user().get("email") or "" + user_email = (await point_session.user()).get("email") or "" return self.async_create_entry( title=user_email, diff --git a/homeassistant/components/point/manifest.json b/homeassistant/components/point/manifest.json index d68acc4832c..6c25cdef91a 100644 --- a/homeassistant/components/point/manifest.json +++ b/homeassistant/components/point/manifest.json @@ -3,7 +3,7 @@ "name": "Minut Point", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/point", - "requirements": ["pypoint==1.1.2"], + "requirements": ["pypoint==2.0.0"], "dependencies": ["webhook", "http"], "codeowners": ["@fredrike"], "quality_scale": "gold" diff --git a/homeassistant/components/point/sensor.py b/homeassistant/components/point/sensor.py index 9436877e434..87f9a8ab2fe 100644 --- a/homeassistant/components/point/sensor.py +++ b/homeassistant/components/point/sensor.py @@ -57,13 +57,11 @@ class MinutPointSensor(MinutPointEntity): async def _update_callback(self): """Update the value of the sensor.""" + _LOGGER.debug("Update sensor value for %s", self) if self.is_updated: - _LOGGER.debug("Update sensor value for %s", self) - self._value = await self.hass.async_add_executor_job( - self.device.sensor, self.device_class - ) + self._value = await self.device.sensor(self.device_class) self._updated = parse_datetime(self.device.last_update) - self.async_write_ha_state() + self.async_write_ha_state() @property def icon(self): diff --git a/requirements_all.txt b/requirements_all.txt index 1c1cab191b5..ad5a35aeedd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1600,7 +1600,7 @@ pypck==0.7.2 pypjlink2==1.2.1 # homeassistant.components.point -pypoint==1.1.2 +pypoint==2.0.0 # homeassistant.components.profiler pyprof2calltree==1.4.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d2927b71046..2c703994191 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -783,7 +783,7 @@ pyowm==2.10.0 pyownet==0.10.0.post1 # homeassistant.components.point -pypoint==1.1.2 +pypoint==2.0.0 # homeassistant.components.profiler pyprof2calltree==1.4.5 diff --git a/tests/components/point/test_config_flow.py b/tests/components/point/test_config_flow.py index d1f8688da24..b6c780e937a 100644 --- a/tests/components/point/test_config_flow.py +++ b/tests/components/point/test_config_flow.py @@ -33,11 +33,13 @@ def mock_pypoint(is_authorized): # pylint: disable=redefined-outer-name with patch( "homeassistant.components.point.config_flow.PointSession" ) as PointSession: - PointSession.return_value.get_access_token.return_value = { - "access_token": "boo" - } + PointSession.return_value.get_access_token = AsyncMock( + return_value={"access_token": "boo"} + ) PointSession.return_value.is_authorized = is_authorized - PointSession.return_value.user.return_value = {"email": "john.doe@example.com"} + PointSession.return_value.user = AsyncMock( + return_value={"email": "john.doe@example.com"} + ) yield PointSession