Remove deprecated yaml import from Ecovacs (#123605)

This commit is contained in:
G Johansson 2024-08-12 09:09:51 +02:00 committed by GitHub
parent 6343a086e4
commit 401e36b885
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 8 additions and 311 deletions

View file

@ -1,31 +1,13 @@
"""Support for Ecovacs Deebot vacuums."""
from sucks import VacBot
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import CONF_CONTINENT, DOMAIN
from .controller import EcovacsController
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_COUNTRY): vol.All(vol.Lower, cv.string),
vol.Required(CONF_CONTINENT): vol.All(vol.Lower, cv.string),
}
)
},
extra=vol.ALLOW_EXTRA,
)
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
@ -41,17 +23,6 @@ PLATFORMS = [
type EcovacsConfigEntry = ConfigEntry[EcovacsController]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Ecovacs component."""
if DOMAIN in config:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=config[DOMAIN]
)
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: EcovacsConfigEntry) -> bool:
"""Set up this integration using UI."""
controller = EcovacsController(hass, entry.data)

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import logging
import ssl
from typing import Any, cast
from typing import Any
from urllib.parse import urlparse
from aiohttp import ClientError
@ -13,21 +13,16 @@ from deebot_client.const import UNDEFINED, UndefinedType
from deebot_client.exceptions import InvalidAuthenticationError, MqttError
from deebot_client.mqtt_client import MqttClient, create_mqtt_config
from deebot_client.util import md5
from deebot_client.util.continents import COUNTRIES_TO_CONTINENTS, get_continent
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_COUNTRY, CONF_MODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, selector
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import VolDictType
from homeassistant.loader import async_get_issue_tracker
from homeassistant.util.ssl import get_default_no_verify_context
from .const import (
CONF_CONTINENT,
CONF_OVERRIDE_MQTT_URL,
CONF_OVERRIDE_REST_URL,
CONF_VERIFY_MQTT_CERTIFICATE,
@ -218,98 +213,3 @@ class EcovacsConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
last_step=True,
)
async def async_step_import(self, user_input: dict[str, Any]) -> ConfigFlowResult:
"""Import configuration from yaml."""
def create_repair(
error: str | None = None, placeholders: dict[str, Any] | None = None
) -> None:
if placeholders is None:
placeholders = {}
if error:
async_create_issue(
self.hass,
DOMAIN,
f"deprecated_yaml_import_issue_{error}",
breaks_in_ha_version="2024.8.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key=f"deprecated_yaml_import_issue_{error}",
translation_placeholders=placeholders
| {"url": "/config/integrations/dashboard/add?domain=ecovacs"},
)
else:
async_create_issue(
self.hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_yaml_{DOMAIN}",
breaks_in_ha_version="2024.8.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders=placeholders
| {
"domain": DOMAIN,
"integration_title": "Ecovacs",
},
)
# We need to validate the imported country and continent
# as the YAML configuration allows any string for them.
# The config flow allows only valid alpha-2 country codes
# through the CountrySelector.
# The continent will be calculated with the function get_continent
# from the country code and there is no need to specify the continent anymore.
# As the YAML configuration includes the continent,
# we check if both the entered continent and the calculated continent match.
# If not we will inform the user about the mismatch.
error = None
placeholders = None
# Convert the country to upper case as ISO 3166-1 alpha-2 country codes are upper case
user_input[CONF_COUNTRY] = user_input[CONF_COUNTRY].upper()
if len(user_input[CONF_COUNTRY]) != 2:
error = "invalid_country_length"
placeholders = {"countries_url": "https://www.iso.org/obp/ui/#search/code/"}
elif len(user_input[CONF_CONTINENT]) != 2:
error = "invalid_continent_length"
placeholders = {
"continent_list": ",".join(
sorted(set(COUNTRIES_TO_CONTINENTS.values()))
)
}
elif user_input[CONF_CONTINENT].lower() != (
continent := get_continent(user_input[CONF_COUNTRY])
):
error = "continent_not_match"
placeholders = {
"continent": continent,
"github_issue_url": cast(
str, async_get_issue_tracker(self.hass, integration_domain=DOMAIN)
),
}
if error:
create_repair(error, placeholders)
return self.async_abort(reason=error)
# Remove the continent from the user input as it is not needed anymore
user_input.pop(CONF_CONTINENT)
try:
result = await self.async_step_auth(user_input)
except AbortFlow as ex:
if ex.reason == "already_configured":
create_repair()
raise
if errors := result.get("errors"):
error = errors["base"]
create_repair(error)
return self.async_abort(reason=error)
create_repair()
return result

View file

@ -237,32 +237,6 @@
"message": "Getting the positions of the chargers and the device itself is not supported"
}
},
"issues": {
"deprecated_yaml_import_issue_cannot_connect": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there was a connection error when trying to import the YAML configuration.\n\nPlease verify that you have a stable internet connection and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
},
"deprecated_yaml_import_issue_invalid_auth": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there was an authentication error when trying to import the YAML configuration.\n\nCorrect the YAML configuration and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
},
"deprecated_yaml_import_issue_unknown": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there was an unknown error when trying to import the YAML configuration.\n\nEnsure the YAML configuration is correct and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
},
"deprecated_yaml_import_issue_invalid_country_length": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there is an invalid country specified in the YAML configuration.\n\nPlease change the country to the [Alpha-2 code of your country]({countries_url}) and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
},
"deprecated_yaml_import_issue_invalid_continent_length": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there is an invalid continent specified in the YAML configuration.\n\nPlease correct the continent to be one of {continent_list} and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
},
"deprecated_yaml_import_issue_continent_not_match": {
"title": "The Ecovacs YAML configuration import failed",
"description": "Configuring Ecovacs using YAML is being removed but there is an unexpected continent specified in the YAML configuration.\n\nFrom the given country, the continent \"{continent}\" is expected. Change the continent and restart Home Assistant to try again or remove the Ecovacs YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually.\n\nIf the contintent \"{continent}\" is not applicable, please open an issue on [GitHub]({github_issue_url})."
}
},
"selector": {
"installation_mode": {
"options": {

View file

@ -11,28 +11,23 @@ from deebot_client.mqtt_client import create_mqtt_config
import pytest
from homeassistant.components.ecovacs.const import (
CONF_CONTINENT,
CONF_OVERRIDE_MQTT_URL,
CONF_OVERRIDE_REST_URL,
CONF_VERIFY_MQTT_CERTIFICATE,
DOMAIN,
InstanceMode,
)
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.const import CONF_COUNTRY, CONF_MODE, CONF_USERNAME
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_MODE, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import issue_registry as ir
from .const import (
IMPORT_DATA,
VALID_ENTRY_DATA_CLOUD,
VALID_ENTRY_DATA_SELF_HOSTED,
VALID_ENTRY_DATA_SELF_HOSTED_WITH_VALIDATE_CERT,
)
from tests.common import MockConfigEntry
_USER_STEP_SELF_HOSTED = {CONF_MODE: InstanceMode.SELF_HOSTED}
_TEST_FN_AUTH_ARG = "user_input_auth"
@ -303,116 +298,3 @@ async def test_user_flow_self_hosted_error(
mock_setup_entry.assert_called()
mock_authenticator_authenticate.assert_called()
mock_mqtt_client.verify_config.assert_called()
async def test_import_flow(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
mock_setup_entry: AsyncMock,
mock_authenticator_authenticate: AsyncMock,
mock_mqtt_client: Mock,
) -> None:
"""Test importing yaml config."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=IMPORT_DATA.copy(),
)
mock_authenticator_authenticate.assert_called()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == VALID_ENTRY_DATA_CLOUD[CONF_USERNAME]
assert result["data"] == VALID_ENTRY_DATA_CLOUD
assert (HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}") in issue_registry.issues
mock_setup_entry.assert_called()
mock_mqtt_client.verify_config.assert_called()
async def test_import_flow_already_configured(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test importing yaml config where entry already configured."""
entry = MockConfigEntry(domain=DOMAIN, data=VALID_ENTRY_DATA_CLOUD)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=IMPORT_DATA.copy(),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert (HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}") in issue_registry.issues
@pytest.mark.parametrize("show_advanced_options", [True, False])
@pytest.mark.parametrize(
("side_effect", "reason"),
[
(ClientError, "cannot_connect"),
(InvalidAuthenticationError, "invalid_auth"),
(Exception, "unknown"),
],
)
async def test_import_flow_error(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
mock_authenticator_authenticate: AsyncMock,
mock_mqtt_client: Mock,
side_effect: Exception,
reason: str,
show_advanced_options: bool,
) -> None:
"""Test handling invalid connection."""
mock_authenticator_authenticate.side_effect = side_effect
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": SOURCE_IMPORT,
"show_advanced_options": show_advanced_options,
},
data=IMPORT_DATA.copy(),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
assert (
DOMAIN,
f"deprecated_yaml_import_issue_{reason}",
) in issue_registry.issues
mock_authenticator_authenticate.assert_called()
@pytest.mark.parametrize("show_advanced_options", [True, False])
@pytest.mark.parametrize(
("reason", "user_input"),
[
("invalid_country_length", IMPORT_DATA | {CONF_COUNTRY: "too_long"}),
("invalid_country_length", IMPORT_DATA | {CONF_COUNTRY: "a"}), # too short
("invalid_continent_length", IMPORT_DATA | {CONF_CONTINENT: "too_long"}),
("invalid_continent_length", IMPORT_DATA | {CONF_CONTINENT: "a"}), # too short
("continent_not_match", IMPORT_DATA | {CONF_CONTINENT: "AA"}),
],
)
async def test_import_flow_invalid_data(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
reason: str,
user_input: dict[str, Any],
show_advanced_options: bool,
) -> None:
"""Test handling invalid connection."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": SOURCE_IMPORT,
"show_advanced_options": show_advanced_options,
},
data=user_input,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
assert (
DOMAIN,
f"deprecated_yaml_import_issue_{reason}",
) in issue_registry.issues

View file

@ -1,7 +1,6 @@
"""Test init of ecovacs."""
from typing import Any
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import Mock, patch
from deebot_client.exceptions import DeebotError, InvalidAuthenticationError
import pytest
@ -12,9 +11,6 @@ from homeassistant.components.ecovacs.controller import EcovacsController
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component
from .const import IMPORT_DATA
from tests.common import MockConfigEntry
@ -88,32 +84,6 @@ async def test_invalid_auth(
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
@pytest.mark.parametrize(
("config", "config_entries_expected"),
[
({}, 0),
({DOMAIN: IMPORT_DATA.copy()}, 1),
],
ids=["no_config", "import_config"],
)
async def test_async_setup_import(
hass: HomeAssistant,
config: dict[str, Any],
config_entries_expected: int,
mock_setup_entry: AsyncMock,
mock_authenticator_authenticate: AsyncMock,
mock_mqtt_client: Mock,
) -> None:
"""Test async_setup config import."""
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
assert len(hass.config_entries.async_entries(DOMAIN)) == config_entries_expected
assert mock_setup_entry.call_count == config_entries_expected
assert mock_authenticator_authenticate.call_count == config_entries_expected
assert mock_mqtt_client.verify_config.call_count == config_entries_expected
async def test_devices_in_dr(
device_registry: dr.DeviceRegistry,
controller: EcovacsController,