From ebb60f69483650f107b70614d0df5050ac4da5ba Mon Sep 17 00:00:00 2001 From: Chris Xiao <30990835+chrisx8@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:01:29 -0500 Subject: [PATCH 1/4] implement options flow for met --- homeassistant/components/met/__init__.py | 7 ++ homeassistant/components/met/config_flow.py | 92 +++++++++++++++------ homeassistant/components/met/strings.json | 13 +++ 3 files changed, 88 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/met/__init__.py b/homeassistant/components/met/__init__.py index 2ccd456936d19d..c95c3abe05e611 100644 --- a/homeassistant/components/met/__init__.py +++ b/homeassistant/components/met/__init__.py @@ -68,6 +68,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][config_entry.entry_id] = coordinator + config_entry.async_on_unload(config_entry.add_update_listener(async_update_entry)) + await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) return True @@ -85,6 +87,11 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> return unload_ok +async def async_update_entry(hass: HomeAssistant, config_entry: ConfigEntry): + """Reload Met component when options changed.""" + await hass.config_entries.async_reload(config_entry.entry_id) + + class CannotConnect(HomeAssistantError): """Unable to connect to the web site.""" diff --git a/homeassistant/components/met/config_flow.py b/homeassistant/components/met/config_flow.py index baf7269a81dffb..5eec1885b6c979 100644 --- a/homeassistant/components/met/config_flow.py +++ b/homeassistant/components/met/config_flow.py @@ -34,13 +34,30 @@ def configured_instances(hass: HomeAssistant) -> set[str]: return set(entries) -class MetFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): +def _get_data_schema( + name: str | None, + latitude: float | None, + longitude: float | None, + elevation: int | None, +) -> vol.Schema: + """Get a schema with default values.""" + return vol.Schema( + { + vol.Required(CONF_NAME, default=name): str, + vol.Required(CONF_LATITUDE, default=latitude): cv.latitude, + vol.Required(CONF_LONGITUDE, default=longitude): cv.longitude, + vol.Required(CONF_ELEVATION, default=elevation): int, + } + ) + + +class MetConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Met component.""" VERSION = 1 def __init__(self) -> None: - """Init MetFlowHandler.""" + """Init MetConfigFlowHandler.""" self._errors: dict[str, Any] = {} async def async_step_user( @@ -59,30 +76,13 @@ async def async_step_user( ) self._errors[CONF_NAME] = "already_configured" - return await self._show_config_form( - name=HOME_LOCATION_NAME, - latitude=self.hass.config.latitude, - longitude=self.hass.config.longitude, - elevation=self.hass.config.elevation, - ) - - async def _show_config_form( - self, - name: str | None = None, - latitude: float | None = None, - longitude: float | None = None, - elevation: int | None = None, - ) -> FlowResult: - """Show the configuration form to edit location data.""" return self.async_show_form( step_id="user", - data_schema=vol.Schema( - { - vol.Required(CONF_NAME, default=name): str, - vol.Required(CONF_LATITUDE, default=latitude): cv.latitude, - vol.Required(CONF_LONGITUDE, default=longitude): cv.longitude, - vol.Required(CONF_ELEVATION, default=elevation): int, - } + data_schema=_get_data_schema( + name=HOME_LOCATION_NAME, + latitude=self.hass.config.latitude, + longitude=self.hass.config.longitude, + elevation=self.hass.config.elevation, ), errors=self._errors, ) @@ -102,3 +102,47 @@ async def async_step_onboarding( return self.async_create_entry( title=HOME_LOCATION_NAME, data={CONF_TRACK_HOME: True} ) + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> config_entries.OptionsFlow: + """Get the options flow for Met.""" + return MetOptionsFlowHandler(config_entry) + + +class MetOptionsFlowHandler(config_entries.OptionsFlow): + """Options flow for Met component.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize the Met OptionsFlow.""" + self._config_entry = config_entry + self._errors: dict[str, Any] = {} + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure options for Met.""" + + if user_input is not None: + # Update config entry with data from user input + self.hass.config_entries.async_update_entry( + self._config_entry, data=user_input + ) + return self.async_create_entry( + title=self._config_entry.title, data=user_input + ) + + config_data = self._config_entry.data + + return self.async_show_form( + step_id="init", + data_schema=_get_data_schema( + name=config_data.get(CONF_NAME), + latitude=config_data.get(CONF_LATITUDE), + longitude=config_data.get(CONF_LONGITUDE), + elevation=config_data.get(CONF_ELEVATION), + ), + errors=self._errors, + ) diff --git a/homeassistant/components/met/strings.json b/homeassistant/components/met/strings.json index b9d251e21d890f..4fa9c58e4bcc27 100644 --- a/homeassistant/components/met/strings.json +++ b/homeassistant/components/met/strings.json @@ -18,5 +18,18 @@ "abort": { "no_home": "No home coordinates are set in the Home Assistant configuration" } + }, + "options": { + "step": { + "init": { + "title": "[%key:common::config_flow::data::location%]", + "data": { + "name": "[%key:common::config_flow::data::name%]", + "latitude": "[%key:common::config_flow::data::latitude%]", + "longitude": "[%key:common::config_flow::data::longitude%]", + "elevation": "[%key:common::config_flow::data::elevation%]" + } + } + } } } From 51ee308088808cbb26ad4c32836dff9ef9aef6c3 Mon Sep 17 00:00:00 2001 From: Chris Xiao <30990835+chrisx8@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:14:27 -0500 Subject: [PATCH 2/4] add tests for met options flow --- tests/components/met/test_config_flow.py | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/components/met/test_config_flow.py b/tests/components/met/test_config_flow.py index 102a9a8b4ec430..9d2340daa22fea 100644 --- a/tests/components/met/test_config_flow.py +++ b/tests/components/met/test_config_flow.py @@ -9,6 +9,8 @@ from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import HomeAssistant +from . import init_integration + from tests.common import MockConfigEntry @@ -130,3 +132,28 @@ async def test_onboarding_step_abort_no_home( assert result["type"] == "abort" assert result["reason"] == "no_home" + + +async def test_show_options_form(hass: HomeAssistant) -> None: + """Test show options form.""" + entry = await init_integration(hass, track_home=True) + + result = await hass.config_entries.options.async_init(entry.entry_id) + + assert result["type"] == "form" + assert result["step_id"] == "init" + + +async def test_options_update_config_entry(hass: HomeAssistant) -> None: + """Test options flow updating config entry.""" + entry = await init_integration(hass, track_home=True) + + update_data = {"name": "test", "latitude": 12, "longitude": 23, "elevation": 456} + + result = await hass.config_entries.options.async_init( + entry.entry_id, data=update_data + ) + + assert result["type"] == "create_entry" + assert result["title"] == "Mock Title" + assert result["data"] == update_data From dfe08979b1512bbd4ad066e435ae51baf658267e Mon Sep 17 00:00:00 2001 From: Chris Xiao <30990835+chrisx8@users.noreply.github.com> Date: Tue, 21 Feb 2023 10:30:20 -0500 Subject: [PATCH 3/4] fix met options flow tests --- tests/components/met/test_config_flow.py | 46 ++++++++++++++---------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/tests/components/met/test_config_flow.py b/tests/components/met/test_config_flow.py index 9d2340daa22fea..59ffff14f1bc38 100644 --- a/tests/components/met/test_config_flow.py +++ b/tests/components/met/test_config_flow.py @@ -1,12 +1,12 @@ """Tests for Met.no config flow.""" -from unittest.mock import patch +from unittest.mock import ANY, patch import pytest from homeassistant import config_entries from homeassistant.components.met.const import DOMAIN, HOME_LOCATION_NAME from homeassistant.config import async_process_ha_core_config -from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.core import HomeAssistant from . import init_integration @@ -15,10 +15,13 @@ @pytest.fixture(name="met_setup", autouse=True) -def met_setup_fixture(): +def met_setup_fixture(request): """Patch met setup entry.""" - with patch("homeassistant.components.met.async_setup_entry", return_value=True): + if "disable_autouse_fixture" in request.keywords: yield + else: + with patch("homeassistant.components.met.async_setup_entry", return_value=True): + yield async def test_show_config_form(hass: HomeAssistant) -> None: @@ -134,26 +137,33 @@ async def test_onboarding_step_abort_no_home( assert result["reason"] == "no_home" -async def test_show_options_form(hass: HomeAssistant) -> None: +@pytest.mark.disable_autouse_fixture +async def test_options_flow(hass: HomeAssistant) -> None: """Test show options form.""" - entry = await init_integration(hass, track_home=True) + update_data = { + CONF_NAME: "test", + CONF_LATITUDE: 12, + CONF_LONGITUDE: 23, + CONF_ELEVATION: 456, + } - result = await hass.config_entries.options.async_init(entry.entry_id) + entry = await init_integration(hass) + await hass.async_block_till_done() + # Test show Options form + result = await hass.config_entries.options.async_init(entry.entry_id) assert result["type"] == "form" assert result["step_id"] == "init" - -async def test_options_update_config_entry(hass: HomeAssistant) -> None: - """Test options flow updating config entry.""" - entry = await init_integration(hass, track_home=True) - - update_data = {"name": "test", "latitude": 12, "longitude": 23, "elevation": 456} - - result = await hass.config_entries.options.async_init( - entry.entry_id, data=update_data - ) - + # Test Options flow updated config entry + with patch("homeassistant.components.met.metno.MetWeatherData") as weatherdatamock: + result = await hass.config_entries.options.async_init( + entry.entry_id, data=update_data + ) + await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == "Mock Title" assert result["data"] == update_data + weatherdatamock.assert_called_with( + {"lat": "12", "lon": "23", "msl": "456"}, ANY, api_url=ANY + ) From e0a82928f0ee62b11139e21ac344a22232fb7c4b Mon Sep 17 00:00:00 2001 From: Chris Xiao <30990835+chrisx8@users.noreply.github.com> Date: Tue, 21 Feb 2023 21:41:56 -0500 Subject: [PATCH 4/4] fix met options flow when tracking home --- homeassistant/components/met/config_flow.py | 48 +++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/met/config_flow.py b/homeassistant/components/met/config_flow.py index 5eec1885b6c979..453c0a9cee80e5 100644 --- a/homeassistant/components/met/config_flow.py +++ b/homeassistant/components/met/config_flow.py @@ -35,18 +35,34 @@ def configured_instances(hass: HomeAssistant) -> set[str]: def _get_data_schema( - name: str | None, - latitude: float | None, - longitude: float | None, - elevation: int | None, + hass: HomeAssistant, config_entry: config_entries.ConfigEntry | None = None ) -> vol.Schema: """Get a schema with default values.""" + # If tracking home or no config entry is passed in, default value come from Home location + if config_entry is None or config_entry.data.get(CONF_TRACK_HOME, False): + return vol.Schema( + { + vol.Required(CONF_NAME, default=HOME_LOCATION_NAME): str, + vol.Required(CONF_LATITUDE, default=hass.config.latitude): cv.latitude, + vol.Required( + CONF_LONGITUDE, default=hass.config.longitude + ): cv.longitude, + vol.Required(CONF_ELEVATION, default=hass.config.elevation): int, + } + ) + # Not tracking home, default values come from config entry return vol.Schema( { - vol.Required(CONF_NAME, default=name): str, - vol.Required(CONF_LATITUDE, default=latitude): cv.latitude, - vol.Required(CONF_LONGITUDE, default=longitude): cv.longitude, - vol.Required(CONF_ELEVATION, default=elevation): int, + vol.Required(CONF_NAME, default=config_entry.data.get(CONF_NAME)): str, + vol.Required( + CONF_LATITUDE, default=config_entry.data.get(CONF_LATITUDE) + ): cv.latitude, + vol.Required( + CONF_LONGITUDE, default=config_entry.data.get(CONF_LONGITUDE) + ): cv.longitude, + vol.Required( + CONF_ELEVATION, default=config_entry.data.get(CONF_ELEVATION) + ): int, } ) @@ -78,12 +94,7 @@ async def async_step_user( return self.async_show_form( step_id="user", - data_schema=_get_data_schema( - name=HOME_LOCATION_NAME, - latitude=self.hass.config.latitude, - longitude=self.hass.config.longitude, - elevation=self.hass.config.elevation, - ), + data_schema=_get_data_schema(self.hass), errors=self._errors, ) @@ -134,15 +145,8 @@ async def async_step_init( title=self._config_entry.title, data=user_input ) - config_data = self._config_entry.data - return self.async_show_form( step_id="init", - data_schema=_get_data_schema( - name=config_data.get(CONF_NAME), - latitude=config_data.get(CONF_LATITUDE), - longitude=config_data.get(CONF_LONGITUDE), - elevation=config_data.get(CONF_ELEVATION), - ), + data_schema=_get_data_schema(self.hass, config_entry=self._config_entry), errors=self._errors, )