From 6a5895222ec908acad3cf478897ca2455f88f730 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sat, 9 Oct 2021 05:23:40 -0400 Subject: [PATCH] Catch errors for efergy (#57326) Co-authored-by: Martin Hjelmare --- homeassistant/components/efergy/sensor.py | 27 +++++++++-- tests/components/efergy/test_sensor.py | 56 +++++++++++++++++++++-- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/efergy/sensor.py b/homeassistant/components/efergy/sensor.py index 338609cf342..a11fe5f3ac6 100644 --- a/homeassistant/components/efergy/sensor.py +++ b/homeassistant/components/efergy/sensor.py @@ -1,7 +1,9 @@ """Support for Efergy sensors.""" from __future__ import annotations -from pyefergy import Efergy +import logging + +from pyefergy import Efergy, exceptions import voluptuous as vol from homeassistant.components.sensor import ( @@ -20,6 +22,7 @@ from homeassistant.const import ( POWER_WATT, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -39,6 +42,8 @@ CONF_CURRENT_VALUES = "current_values" DEFAULT_PERIOD = "year" DEFAULT_UTC_OFFSET = "0" +_LOGGER = logging.getLogger(__name__) + SENSOR_TYPES: dict[str, SensorEntityDescription] = { CONF_INSTANT: SensorEntityDescription( key=CONF_INSTANT, @@ -102,7 +107,10 @@ async def async_setup_platform( ) dev = [] - sensors = await api.get_sids() + try: + sensors = await api.get_sids() + except (exceptions.DataError, exceptions.ConnectTimeout) as ex: + raise PlatformNotReady("Error getting data from Efergy:") from ex for variable in config[CONF_MONITORED_VARIABLES]: if variable[CONF_TYPE] == CONF_CURRENT_VALUES: for sensor in sensors: @@ -150,6 +158,15 @@ class EfergySensor(SensorEntity): async def async_update(self) -> None: """Get the Efergy monitor data from the web service.""" - self._attr_native_value = await self.api.async_get_reading( - self.entity_description.key, period=self.period, sid=self.sid - ) + try: + self._attr_native_value = await self.api.async_get_reading( + self.entity_description.key, period=self.period, sid=self.sid + ) + except (exceptions.DataError, exceptions.ConnectTimeout) as ex: + if self._attr_available: + self._attr_available = False + _LOGGER.error("Error getting data from Efergy: %s", ex) + return + if not self._attr_available: + self._attr_available = True + _LOGGER.info("Connection to Efergy has resumed") diff --git a/tests/components/efergy/test_sensor.py b/tests/components/efergy/test_sensor.py index 97478155483..94a381b9048 100644 --- a/tests/components/efergy/test_sensor.py +++ b/tests/components/efergy/test_sensor.py @@ -1,9 +1,15 @@ """The tests for Efergy sensor platform.""" +import asyncio +from datetime import timedelta + +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util -from tests.common import load_fixture +from tests.common import async_fire_time_changed, load_fixture from tests.test_util.aiohttp import AiohttpClientMocker token = "9p6QGJ7dpZfO3fqPTBk1fyEmjV1cGoLT" @@ -30,9 +36,14 @@ MULTI_SENSOR_CONFIG = { } -def mock_responses(aioclient_mock: AiohttpClientMocker): +def mock_responses(aioclient_mock: AiohttpClientMocker, error: bool = False): """Mock responses for Efergy.""" base_url = "https://engage.efergy.com/mobile_proxy/" + if error: + aioclient_mock.get( + f"{base_url}getCurrentValuesSummary?token={token}", exc=asyncio.TimeoutError + ) + return aioclient_mock.get( f"{base_url}getInstant?token={token}", text=load_fixture("efergy/efergy_instant.json"), @@ -64,7 +75,9 @@ async def test_single_sensor_readings( ): """Test for successfully setting up the Efergy platform.""" mock_responses(aioclient_mock) - assert await async_setup_component(hass, "sensor", {"sensor": ONE_SENSOR_CONFIG}) + assert await async_setup_component( + hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG} + ) await hass.async_block_till_done() assert hass.states.get("sensor.energy_consumed").state == "38.21" @@ -79,9 +92,44 @@ async def test_multi_sensor_readings( ): """Test for multiple sensors in one household.""" mock_responses(aioclient_mock) - assert await async_setup_component(hass, "sensor", {"sensor": MULTI_SENSOR_CONFIG}) + assert await async_setup_component( + hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: MULTI_SENSOR_CONFIG} + ) await hass.async_block_till_done() assert hass.states.get("sensor.efergy_728386").state == "218" assert hass.states.get("sensor.efergy_0").state == "1808" assert hass.states.get("sensor.efergy_728387").state == "312" + + +async def test_failed_getting_sids( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +): + """Test failed gettings sids.""" + mock_responses(aioclient_mock, error=True) + assert await async_setup_component( + hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG} + ) + assert not hass.states.async_all("sensor") + + +async def test_failed_update_and_reconnection( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +): + """Test failed update and reconnection.""" + mock_responses(aioclient_mock) + assert await async_setup_component( + hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG} + ) + aioclient_mock.clear_requests() + mock_responses(aioclient_mock, error=True) + next_update = dt_util.utcnow() + timedelta(seconds=3) + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + assert hass.states.get("sensor.efergy_728386").state == STATE_UNAVAILABLE + aioclient_mock.clear_requests() + mock_responses(aioclient_mock) + next_update = dt_util.utcnow() + timedelta(seconds=30) + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + assert hass.states.get("sensor.efergy_728386").state == "1628"