diff --git a/homeassistant/components/ipma/__init__.py b/homeassistant/components/ipma/__init__.py index eec16a0c811..866e79cbe40 100644 --- a/homeassistant/components/ipma/__init__.py +++ b/homeassistant/components/ipma/__init__.py @@ -57,4 +57,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + if not hass.data[DOMAIN]: + hass.data.pop(DOMAIN) + + return unload_ok diff --git a/homeassistant/components/ipma/const.py b/homeassistant/components/ipma/const.py index 60c8115a5c4..515fb501fbd 100644 --- a/homeassistant/components/ipma/const.py +++ b/homeassistant/components/ipma/const.py @@ -5,7 +5,7 @@ DOMAIN = "ipma" HOME_LOCATION_NAME = "Home" -ENTITY_ID_SENSOR_FORMAT_HOME = f"{WEATHER_DOMAIN}.ipma_{HOME_LOCATION_NAME}" - -DATA_LOCATION = "location" DATA_API = "api" +DATA_LOCATION = "location" + +ENTITY_ID_SENSOR_FORMAT_HOME = f"{WEATHER_DOMAIN}.ipma_{HOME_LOCATION_NAME}" diff --git a/homeassistant/components/ipma/weather.py b/homeassistant/components/ipma/weather.py index a0fe5b235b3..c448fad592d 100644 --- a/homeassistant/components/ipma/weather.py +++ b/homeassistant/components/ipma/weather.py @@ -8,7 +8,6 @@ import async_timeout from pyipma.api import IPMA_API from pyipma.forecast import Forecast from pyipma.location import Location -import voluptuous as vol from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, @@ -33,13 +32,10 @@ from homeassistant.components.weather import ( ATTR_FORECAST_PRECIPITATION_PROBABILITY, ATTR_FORECAST_TIME, ATTR_FORECAST_WIND_BEARING, - PLATFORM_SCHEMA, WeatherEntity, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_LATITUDE, - CONF_LONGITUDE, CONF_MODE, CONF_NAME, PRESSURE_HPA, @@ -47,7 +43,7 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv, entity_registry +from homeassistant.helpers import entity_registry from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.sun import is_up from homeassistant.util import Throttle @@ -80,15 +76,6 @@ CONDITION_CLASSES = { FORECAST_MODE = ["hourly", "daily"] -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_LATITUDE): cv.latitude, - vol.Optional(CONF_LONGITUDE): cv.longitude, - vol.Optional(CONF_MODE, default="daily"): vol.In(FORECAST_MODE), - } -) - async def async_setup_entry( hass: HomeAssistant, diff --git a/tests/components/ipma/__init__.py b/tests/components/ipma/__init__.py index 35099c405bb..4a002140437 100644 --- a/tests/components/ipma/__init__.py +++ b/tests/components/ipma/__init__.py @@ -1 +1,111 @@ """Tests for the IPMA component.""" +from collections import namedtuple +from datetime import datetime, timezone + +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE, CONF_NAME + +ENTRY_CONFIG = { + CONF_NAME: "Home Town", + CONF_LATITUDE: "1", + CONF_LONGITUDE: "2", + CONF_MODE: "hourly", +} + + +class MockLocation: + """Mock Location from pyipma.""" + + async def observation(self, api): + """Mock Observation.""" + Observation = namedtuple( + "Observation", + [ + "accumulated_precipitation", + "humidity", + "pressure", + "radiation", + "temperature", + "wind_direction", + "wind_intensity_km", + ], + ) + + return Observation(0.0, 71.0, 1000.0, 0.0, 18.0, "NW", 3.94) + + async def forecast(self, api, period): + """Mock Forecast.""" + Forecast = namedtuple( + "Forecast", + [ + "feels_like_temperature", + "forecast_date", + "forecasted_hours", + "humidity", + "max_temperature", + "min_temperature", + "precipitation_probability", + "temperature", + "update_date", + "weather_type", + "wind_direction", + "wind_strength", + ], + ) + + WeatherType = namedtuple("WeatherType", ["id", "en", "pt"]) + + if period == 24: + return [ + Forecast( + None, + datetime(2020, 1, 16, 0, 0, 0), + 24, + None, + 16.2, + 10.6, + "100.0", + 13.4, + "2020-01-15T07:51:00", + WeatherType(9, "Rain/showers", "Chuva/aguaceiros"), + "S", + "10", + ), + ] + if period == 1: + return [ + Forecast( + "7.7", + datetime(2020, 1, 15, 1, 0, 0, tzinfo=timezone.utc), + 1, + "86.9", + 12.0, + None, + 80.0, + 10.6, + "2020-01-15T02:51:00", + WeatherType(10, "Light rain", "Chuva fraca ou chuvisco"), + "S", + "32.7", + ), + Forecast( + "5.7", + datetime(2020, 1, 15, 2, 0, 0, tzinfo=timezone.utc), + 1, + "86.9", + 12.0, + None, + 80.0, + 10.6, + "2020-01-15T02:51:00", + WeatherType(1, "Clear sky", "C\u00e9u limpo"), + "S", + "32.7", + ), + ] + + name = "HomeTown" + station = "HomeTown Station" + station_latitude = 0 + station_longitude = 0 + global_id_local = 1130600 + id_station = 1200545 diff --git a/tests/components/ipma/test_config_flow.py b/tests/components/ipma/test_config_flow.py index ea4b0b510e7..e254ba402fb 100644 --- a/tests/components/ipma/test_config_flow.py +++ b/tests/components/ipma/test_config_flow.py @@ -3,21 +3,14 @@ from unittest.mock import Mock, patch from homeassistant.components.ipma import DOMAIN, config_flow -from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE, CONF_NAME +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component -from .test_weather import MockLocation +from . import MockLocation from tests.common import MockConfigEntry, mock_registry -ENTRY_CONFIG = { - CONF_NAME: "Home Town", - CONF_LATITUDE: "1", - CONF_LONGITUDE: "2", - CONF_MODE: "hourly", -} - async def test_show_config_form(): """Test show configuration form.""" diff --git a/tests/components/ipma/test_init.py b/tests/components/ipma/test_init.py new file mode 100644 index 00000000000..8dd808b1b1b --- /dev/null +++ b/tests/components/ipma/test_init.py @@ -0,0 +1,57 @@ +"""Test the IPMA integration.""" + +from unittest.mock import patch + +from pyipma import IPMAException + +from homeassistant.components.ipma import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE + +from .test_weather import MockLocation + +from tests.common import MockConfigEntry + + +async def test_async_setup_raises_entry_not_ready(hass): + """Test that it throws ConfigEntryNotReady when exception occurs during setup.""" + + with patch( + "pyipma.location.Location.get", side_effect=IPMAException("API unavailable") + ): + + config_entry = MockConfigEntry( + domain=DOMAIN, + title="Home", + data={CONF_LATITUDE: 0, CONF_LONGITUDE: 0, CONF_MODE: "daily"}, + ) + + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + + assert config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_unload_config_entry(hass): + """Test entry unloading.""" + + with patch( + "pyipma.location.Location.get", + return_value=MockLocation(), + ): + config_entry = MockConfigEntry( + domain="ipma", + data={CONF_LATITUDE: 0, CONF_LONGITUDE: 0, CONF_MODE: "daily"}, + ) + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.LOADED + + await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.NOT_LOADED diff --git a/tests/components/ipma/test_weather.py b/tests/components/ipma/test_weather.py index e129216730d..62450871ee8 100644 --- a/tests/components/ipma/test_weather.py +++ b/tests/components/ipma/test_weather.py @@ -1,6 +1,5 @@ """The tests for the IPMA weather component.""" -from collections import namedtuple -from datetime import datetime, timezone +from datetime import datetime from unittest.mock import patch from freezegun import freeze_time @@ -22,6 +21,8 @@ from homeassistant.components.weather import ( ) from homeassistant.const import STATE_UNKNOWN +from . import MockLocation + from tests.common import MockConfigEntry TEST_CONFIG = { @@ -39,128 +40,6 @@ TEST_CONFIG_HOURLY = { } -class MockLocation: - """Mock Location from pyipma.""" - - async def observation(self, api): - """Mock Observation.""" - Observation = namedtuple( - "Observation", - [ - "accumulated_precipitation", - "humidity", - "pressure", - "radiation", - "temperature", - "wind_direction", - "wind_intensity_km", - ], - ) - - return Observation(0.0, 71.0, 1000.0, 0.0, 18.0, "NW", 3.94) - - async def forecast(self, api, period): - """Mock Forecast.""" - Forecast = namedtuple( - "Forecast", - [ - "feels_like_temperature", - "forecast_date", - "forecasted_hours", - "humidity", - "max_temperature", - "min_temperature", - "precipitation_probability", - "temperature", - "update_date", - "weather_type", - "wind_direction", - "wind_strength", - ], - ) - - WeatherType = namedtuple("WeatherType", ["id", "en", "pt"]) - - if period == 24: - return [ - Forecast( - None, - datetime(2020, 1, 16, 0, 0, 0), - 24, - None, - 16.2, - 10.6, - "100.0", - 13.4, - "2020-01-15T07:51:00", - WeatherType(9, "Rain/showers", "Chuva/aguaceiros"), - "S", - "10", - ), - ] - if period == 1: - return [ - Forecast( - "7.7", - datetime(2020, 1, 15, 1, 0, 0, tzinfo=timezone.utc), - 1, - "86.9", - 12.0, - None, - 80.0, - 10.6, - "2020-01-15T02:51:00", - WeatherType(10, "Light rain", "Chuva fraca ou chuvisco"), - "S", - "32.7", - ), - Forecast( - "5.7", - datetime(2020, 1, 15, 2, 0, 0, tzinfo=timezone.utc), - 1, - "86.9", - 12.0, - None, - 80.0, - 10.6, - "2020-01-15T02:51:00", - WeatherType(1, "Clear sky", "C\u00e9u limpo"), - "S", - "32.7", - ), - ] - - @property - def name(self): - """Mock location.""" - return "HomeTown" - - @property - def station(self): - """Mock station.""" - return "HomeTown Station" - - @property - def station_latitude(self): - """Mock latitude.""" - return 0 - - @property - def global_id_local(self): - """Mock global identifier of the location.""" - return 1130600 - - @property - def id_station(self): - """Mock identifier of the station.""" - return 1200545 - - @property - def station_longitude(self): - """Mock longitude.""" - return 0 - - class MockBadLocation(MockLocation): """Mock Location with unresponsive api."""