From a2080492dec0f35e8091922eef93f4a8baac64da Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 21 Oct 2022 18:07:49 +0200 Subject: [PATCH] Remove Xiaomi Miio YAML import (#78995) * Deprecate YAML import * Add logging for unexpected errors * remove unused import * fix tests * unused import * fix tests * fix snake_case * Do not add to standard key string --- .../components/xiaomi_miio/__init__.py | 4 - .../components/xiaomi_miio/config_flow.py | 34 ++++---- .../components/xiaomi_miio/strings.json | 3 +- .../xiaomi_miio/translations/en.json | 3 +- .../xiaomi_miio/test_config_flow.py | 84 +++++++++++-------- 5 files changed, 70 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index 8719319aec8..d3a407d529e 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -383,10 +383,6 @@ async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) -> assert gateway_id - # For backwards compat - if gateway_id.endswith("-gateway"): - hass.config_entries.async_update_entry(entry, unique_id=entry.data["mac"]) - # Connect to gateway gateway = ConnectXiaomiGateway(hass, entry) try: diff --git a/homeassistant/components/xiaomi_miio/config_flow.py b/homeassistant/components/xiaomi_miio/config_flow.py index 4e2ba24bc05..70e6fb5c0b6 100644 --- a/homeassistant/components/xiaomi_miio/config_flow.py +++ b/homeassistant/components/xiaomi_miio/config_flow.py @@ -13,7 +13,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components import zeroconf from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry -from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_NAME, CONF_TOKEN +from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_TOKEN from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.device_registry import format_mac @@ -145,18 +145,6 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return await self.async_step_cloud() return self.async_show_form(step_id="reauth_confirm") - async def async_step_import(self, conf: dict[str, Any]) -> FlowResult: - """Import a configuration from config.yaml.""" - self.host = conf[CONF_HOST] - self.token = conf[CONF_TOKEN] - self.name = conf.get(CONF_NAME) - self.model = conf.get(CONF_MODEL) - - self.context.update( - {"title_placeholders": {"name": f"YAML import {self.host}"}} - ) - return await self.async_step_connect() - async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -250,15 +238,22 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "cloud_login_error" except MiCloudAccessDenied: errors["base"] = "cloud_login_error" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception in Miio cloud login") + return self.async_abort(reason="unknown") if errors: return self.async_show_form( step_id="cloud", data_schema=DEVICE_CLOUD_CONFIG, errors=errors ) - devices_raw = await self.hass.async_add_executor_job( - miio_cloud.get_devices, cloud_country - ) + try: + devices_raw = await self.hass.async_add_executor_job( + miio_cloud.get_devices, cloud_country + ) + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception in Miio cloud get devices") + return self.async_abort(reason="unknown") if not devices_raw: errors["base"] = "cloud_no_devices" @@ -353,6 +348,9 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): except SetupException: if self.model is None: errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception in connect Xiaomi device") + return self.async_abort(reason="unknown") device_info = connect_device_class.device_info @@ -386,8 +384,8 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data[CONF_CLOUD_USERNAME] = self.cloud_username data[CONF_CLOUD_PASSWORD] = self.cloud_password data[CONF_CLOUD_COUNTRY] = self.cloud_country - self.hass.config_entries.async_update_entry(existing_entry, data=data) - await self.hass.config_entries.async_reload(existing_entry.entry_id) + if self.hass.config_entries.async_update_entry(existing_entry, data=data): + await self.hass.config_entries.async_reload(existing_entry.entry_id) return self.async_abort(reason="reauth_successful") if self.name is None: diff --git a/homeassistant/components/xiaomi_miio/strings.json b/homeassistant/components/xiaomi_miio/strings.json index e359f54cc5a..ea9e1712697 100644 --- a/homeassistant/components/xiaomi_miio/strings.json +++ b/homeassistant/components/xiaomi_miio/strings.json @@ -5,7 +5,8 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "incomplete_info": "Incomplete information to setup device, no host or token supplied.", - "not_xiaomi_miio": "Device is not (yet) supported by Xiaomi Miio." + "not_xiaomi_miio": "Device is not (yet) supported by Xiaomi Miio.", + "unknown": "[%key:common::config_flow::error::unknown%]" }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", diff --git a/homeassistant/components/xiaomi_miio/translations/en.json b/homeassistant/components/xiaomi_miio/translations/en.json index c37be0a7f74..d24509e0e25 100644 --- a/homeassistant/components/xiaomi_miio/translations/en.json +++ b/homeassistant/components/xiaomi_miio/translations/en.json @@ -5,7 +5,8 @@ "already_in_progress": "Configuration flow is already in progress", "incomplete_info": "Incomplete information to setup device, no host or token supplied.", "not_xiaomi_miio": "Device is not (yet) supported by Xiaomi Miio.", - "reauth_successful": "Re-authentication was successful" + "reauth_successful": "Re-authentication was successful", + "unknown": "Unexpected error" }, "error": { "cannot_connect": "Failed to connect", diff --git a/tests/components/xiaomi_miio/test_config_flow.py b/tests/components/xiaomi_miio/test_config_flow.py index e47a1a1ace5..9d8a8b39167 100644 --- a/tests/components/xiaomi_miio/test_config_flow.py +++ b/tests/components/xiaomi_miio/test_config_flow.py @@ -9,7 +9,7 @@ import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components import zeroconf from homeassistant.components.xiaomi_miio import const -from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_NAME, CONF_TOKEN +from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_TOKEN from . import TEST_MAC @@ -67,7 +67,7 @@ TEST_CLOUD_DEVICES_2 = [ @pytest.fixture(name="xiaomi_miio_connect", autouse=True) def xiaomi_miio_connect_fixture(): - """Mock denonavr connection and entry setup.""" + """Mock miio connection and entry setup.""" mock_info = get_mock_info() with patch( @@ -320,6 +320,22 @@ async def test_config_flow_gateway_cloud_login_error(hass): assert result["step_id"] == "cloud" assert result["errors"] == {"base": "cloud_login_error"} + with patch( + "homeassistant.components.xiaomi_miio.config_flow.MiCloud.login", + side_effect=Exception({}), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER, + const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS, + const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY, + }, + ) + + assert result["type"] == "abort" + assert result["reason"] == "unknown" + async def test_config_flow_gateway_cloud_no_devices(hass): """Test a failed config flow using cloud with no devices.""" @@ -348,6 +364,22 @@ async def test_config_flow_gateway_cloud_no_devices(hass): assert result["step_id"] == "cloud" assert result["errors"] == {"base": "cloud_no_devices"} + with patch( + "homeassistant.components.xiaomi_miio.config_flow.MiCloud.get_devices", + side_effect=Exception({}), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER, + const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS, + const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY, + }, + ) + + assert result["type"] == "abort" + assert result["reason"] == "unknown" + async def test_config_flow_gateway_cloud_missing_token(hass): """Test a failed config flow using cloud with a missing token.""" @@ -558,34 +590,6 @@ async def test_config_flow_step_unknown_device(hass): assert result["errors"] == {"base": "unknown_device"} -async def test_import_flow_success(hass): - """Test a successful import form yaml for a device.""" - mock_info = get_mock_info(model=const.MODELS_SWITCH[0]) - - with patch( - "homeassistant.components.xiaomi_miio.device.Device.info", - return_value=mock_info, - ): - result = await hass.config_entries.flow.async_init( - const.DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data={CONF_NAME: TEST_NAME, CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_TOKEN}, - ) - - assert result["type"] == "create_entry" - assert result["title"] == TEST_NAME - assert result["data"] == { - const.CONF_FLOW_TYPE: const.CONF_DEVICE, - const.CONF_CLOUD_USERNAME: None, - const.CONF_CLOUD_PASSWORD: None, - const.CONF_CLOUD_COUNTRY: None, - CONF_HOST: TEST_HOST, - CONF_TOKEN: TEST_TOKEN, - CONF_MODEL: const.MODELS_SWITCH[0], - const.CONF_MAC: TEST_MAC, - } - - async def test_config_flow_step_device_manual_model_error(hass): """Test config flow, device connection error, model None.""" result = await hass.config_entries.flow.async_init( @@ -618,6 +622,18 @@ async def test_config_flow_step_device_manual_model_error(hass): assert result["step_id"] == "connect" assert result["errors"] == {"base": "cannot_connect"} + with patch( + "homeassistant.components.xiaomi_miio.device.Device.info", + side_effect=Exception({}), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_MODEL: TEST_MODEL}, + ) + + assert result["type"] == "abort" + assert result["reason"] == "unknown" + async def test_config_flow_step_device_manual_model_succes(hass): """Test config flow, device connection error, manual model.""" @@ -724,7 +740,7 @@ async def config_flow_device_success(hass, model_to_test): async def config_flow_generic_roborock(hass): """Test a successful config flow for a generic roborock vacuum.""" - DUMMY_MODEL = "roborock.vacuum.dummy" + dummy_model = "roborock.vacuum.dummy" result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -743,7 +759,7 @@ async def config_flow_generic_roborock(hass): assert result["step_id"] == "manual" assert result["errors"] == {} - mock_info = get_mock_info(model=DUMMY_MODEL) + mock_info = get_mock_info(model=dummy_model) with patch( "homeassistant.components.xiaomi_miio.device.Device.info", @@ -755,7 +771,7 @@ async def config_flow_generic_roborock(hass): ) assert result["type"] == "create_entry" - assert result["title"] == DUMMY_MODEL + assert result["title"] == dummy_model assert result["data"] == { const.CONF_FLOW_TYPE: const.CONF_DEVICE, const.CONF_CLOUD_USERNAME: None, @@ -763,7 +779,7 @@ async def config_flow_generic_roborock(hass): const.CONF_CLOUD_COUNTRY: None, CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_TOKEN, - CONF_MODEL: DUMMY_MODEL, + CONF_MODEL: dummy_model, const.CONF_MAC: TEST_MAC, }