Add config flow for Ecovacs (#108111)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
3e72c346b7
commit
7fe6fc987b
15 changed files with 412 additions and 39 deletions
|
@ -272,7 +272,9 @@ omit =
|
|||
homeassistant/components/econet/climate.py
|
||||
homeassistant/components/econet/sensor.py
|
||||
homeassistant/components/econet/water_heater.py
|
||||
homeassistant/components/ecovacs/*
|
||||
homeassistant/components/ecovacs/__init__.py
|
||||
homeassistant/components/ecovacs/util.py
|
||||
homeassistant/components/ecovacs/vacuum.py
|
||||
homeassistant/components/ecowitt/__init__.py
|
||||
homeassistant/components/ecowitt/binary_sensor.py
|
||||
homeassistant/components/ecowitt/entity.py
|
||||
|
|
|
@ -321,7 +321,8 @@ build.json @home-assistant/supervisor
|
|||
/tests/components/ecoforest/ @pjanuario
|
||||
/homeassistant/components/econet/ @w1ll1am23
|
||||
/tests/components/econet/ @w1ll1am23
|
||||
/homeassistant/components/ecovacs/ @OverloadUT @mib1185
|
||||
/homeassistant/components/ecovacs/ @OverloadUT @mib1185 @edenhaus
|
||||
/tests/components/ecovacs/ @OverloadUT @mib1185 @edenhaus
|
||||
/homeassistant/components/ecowitt/ @pvizeli
|
||||
/tests/components/ecowitt/ @pvizeli
|
||||
/homeassistant/components/efergy/ @tkdrob
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
"""Support for Ecovacs Deebot vacuums."""
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
|
||||
from sucks import EcoVacsAPI, VacBot
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_COUNTRY,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_CONTINENT, DOMAIN
|
||||
from .util import get_client_device_id
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "ecovacs"
|
||||
|
||||
CONF_COUNTRY = "country"
|
||||
CONF_CONTINENT = "continent"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -38,32 +36,39 @@ CONFIG_SCHEMA = vol.Schema(
|
|||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
ECOVACS_DEVICES = "ecovacs_devices"
|
||||
|
||||
# Generate a random device ID on each bootup
|
||||
ECOVACS_API_DEVICEID = "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(8)
|
||||
)
|
||||
PLATFORMS = [
|
||||
Platform.VACUUM,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Ecovacs component."""
|
||||
_LOGGER.debug("Creating new 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: ConfigEntry) -> bool:
|
||||
"""Set up this integration using UI."""
|
||||
|
||||
def get_devices() -> list[VacBot]:
|
||||
ecovacs_api = EcoVacsAPI(
|
||||
ECOVACS_API_DEVICEID,
|
||||
config[DOMAIN].get(CONF_USERNAME),
|
||||
EcoVacsAPI.md5(config[DOMAIN].get(CONF_PASSWORD)),
|
||||
config[DOMAIN].get(CONF_COUNTRY),
|
||||
config[DOMAIN].get(CONF_CONTINENT),
|
||||
get_client_device_id(),
|
||||
entry.data[CONF_USERNAME],
|
||||
EcoVacsAPI.md5(entry.data[CONF_PASSWORD]),
|
||||
entry.data[CONF_COUNTRY],
|
||||
entry.data[CONF_CONTINENT],
|
||||
)
|
||||
ecovacs_devices = ecovacs_api.devices()
|
||||
_LOGGER.debug("Ecobot devices: %s", ecovacs_devices)
|
||||
|
||||
_LOGGER.debug("Ecobot devices: %s", ecovacs_devices)
|
||||
devices: list[VacBot] = []
|
||||
for device in ecovacs_devices:
|
||||
_LOGGER.info(
|
||||
_LOGGER.debug(
|
||||
"Discovered Ecovacs device on account: %s with nickname %s",
|
||||
device.get("did"),
|
||||
device.get("nick"),
|
||||
|
@ -74,18 +79,20 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
ecovacs_api.resource,
|
||||
ecovacs_api.user_access_token,
|
||||
device,
|
||||
config[DOMAIN].get(CONF_CONTINENT).lower(),
|
||||
entry.data[CONF_CONTINENT],
|
||||
monitor=True,
|
||||
)
|
||||
|
||||
devices.append(vacbot)
|
||||
return devices
|
||||
|
||||
hass.data[ECOVACS_DEVICES] = await hass.async_add_executor_job(get_devices)
|
||||
hass.data.setdefault(DOMAIN, {})[
|
||||
entry.entry_id
|
||||
] = await hass.async_add_executor_job(get_devices)
|
||||
|
||||
async def async_stop(event: object) -> None:
|
||||
"""Shut down open connections to Ecovacs XMPP server."""
|
||||
devices: list[VacBot] = hass.data[ECOVACS_DEVICES]
|
||||
devices: list[VacBot] = hass.data[DOMAIN][entry.entry_id]
|
||||
for device in devices:
|
||||
_LOGGER.info(
|
||||
"Shutting down connection to Ecovacs device %s",
|
||||
|
@ -96,10 +103,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
# Listen for HA stop to disconnect.
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop)
|
||||
|
||||
if hass.data[ECOVACS_DEVICES]:
|
||||
_LOGGER.debug("Starting vacuum components")
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(hass, Platform.VACUUM, DOMAIN, {}, config)
|
||||
)
|
||||
if hass.data[DOMAIN][entry.entry_id]:
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
|
136
homeassistant/components/ecovacs/config_flow.py
Normal file
136
homeassistant/components/ecovacs/config_flow.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
"""Config flow for Ecovacs mqtt integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from sucks import EcoVacsAPI
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN
|
||||
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||
from homeassistant.helpers import selector
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
|
||||
from .const import CONF_CONTINENT, DOMAIN
|
||||
from .util import get_client_device_id
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def validate_input(user_input: dict[str, Any]) -> dict[str, str]:
|
||||
"""Validate user input."""
|
||||
errors: dict[str, str] = {}
|
||||
try:
|
||||
EcoVacsAPI(
|
||||
get_client_device_id(),
|
||||
user_input[CONF_USERNAME],
|
||||
EcoVacsAPI.md5(user_input[CONF_PASSWORD]),
|
||||
user_input[CONF_COUNTRY],
|
||||
user_input[CONF_CONTINENT],
|
||||
)
|
||||
except ValueError:
|
||||
errors["base"] = "invalid_auth"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
class EcovacsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Ecovacs."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
|
||||
if user_input:
|
||||
self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]})
|
||||
|
||||
errors = await self.hass.async_add_executor_job(validate_input, user_input)
|
||||
|
||||
if not errors:
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_USERNAME], data=user_input
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): selector.TextSelector(
|
||||
selector.TextSelectorConfig(
|
||||
type=selector.TextSelectorType.TEXT
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_PASSWORD): selector.TextSelector(
|
||||
selector.TextSelectorConfig(
|
||||
type=selector.TextSelectorType.PASSWORD
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_COUNTRY): vol.All(vol.Lower, cv.string),
|
||||
vol.Required(CONF_CONTINENT): vol.All(vol.Lower, cv.string),
|
||||
}
|
||||
),
|
||||
user_input,
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
|
||||
"""Import configuration from yaml."""
|
||||
|
||||
def create_repair(error: str | None = None) -> None:
|
||||
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={
|
||||
"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={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "Ecovacs",
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
result = await self.async_step_user(user_input)
|
||||
except AbortFlow as ex:
|
||||
if ex.reason == "already_configured":
|
||||
create_repair()
|
||||
raise ex
|
||||
|
||||
if errors := result.get("errors"):
|
||||
error = errors["base"]
|
||||
create_repair(error)
|
||||
return self.async_abort(reason=error)
|
||||
|
||||
create_repair()
|
||||
return result
|
5
homeassistant/components/ecovacs/const.py
Normal file
5
homeassistant/components/ecovacs/const.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
"""Ecovacs constants."""
|
||||
|
||||
DOMAIN = "ecovacs"
|
||||
|
||||
CONF_CONTINENT = "continent"
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"domain": "ecovacs",
|
||||
"name": "Ecovacs",
|
||||
"codeowners": ["@OverloadUT", "@mib1185"],
|
||||
"codeowners": ["@OverloadUT", "@mib1185", "@edenhaus"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["sleekxmppfs", "sucks"],
|
||||
|
|
35
homeassistant/components/ecovacs/strings.json
Normal file
35
homeassistant/components/ecovacs/strings.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"continent": "Continent",
|
||||
"country": "Country",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
},
|
||||
"data_description": {
|
||||
"continent": "Your two-letter continent code (na, eu, etc)",
|
||||
"country": "Your two-letter country code (us, uk, etc)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
11
homeassistant/components/ecovacs/util.py
Normal file
11
homeassistant/components/ecovacs/util.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
"""Ecovacs util functions."""
|
||||
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
def get_client_device_id() -> str:
|
||||
"""Get client device id."""
|
||||
return "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(8)
|
||||
)
|
|
@ -15,12 +15,12 @@ from homeassistant.components.vacuum import (
|
|||
StateVacuumEntity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import ECOVACS_DEVICES
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -28,15 +28,14 @@ ATTR_ERROR = "error"
|
|||
ATTR_COMPONENT_PREFIX = "component_"
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Ecovacs vacuums."""
|
||||
vacuums = []
|
||||
devices: list[sucks.VacBot] = hass.data[ECOVACS_DEVICES]
|
||||
devices: list[sucks.VacBot] = hass.data[DOMAIN][config_entry.entry_id]
|
||||
for device in devices:
|
||||
await hass.async_add_executor_job(device.connect_and_wait_until_ready)
|
||||
vacuums.append(EcovacsVacuum(device))
|
||||
|
|
|
@ -126,6 +126,7 @@ FLOWS = {
|
|||
"ecobee",
|
||||
"ecoforest",
|
||||
"econet",
|
||||
"ecovacs",
|
||||
"ecowitt",
|
||||
"edl21",
|
||||
"efergy",
|
||||
|
|
|
@ -1381,7 +1381,7 @@
|
|||
"ecovacs": {
|
||||
"name": "Ecovacs",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_push"
|
||||
},
|
||||
"ecowitt": {
|
||||
|
|
|
@ -1230,6 +1230,9 @@ py-nextbusnext==1.0.2
|
|||
# homeassistant.components.nightscout
|
||||
py-nightscout==1.2.2
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
py-sucks==0.9.8
|
||||
|
||||
# homeassistant.components.synology_dsm
|
||||
py-synologydsm-api==2.1.4
|
||||
|
||||
|
|
1
tests/components/ecovacs/__init__.py
Normal file
1
tests/components/ecovacs/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Tests for the Ecovacs integration."""
|
14
tests/components/ecovacs/conftest.py
Normal file
14
tests/components/ecovacs/conftest.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Common fixtures for the Ecovacs tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.ecovacs.async_setup_entry", return_value=True
|
||||
) as async_setup_entry:
|
||||
yield async_setup_entry
|
160
tests/components/ecovacs/test_config_flow.py
Normal file
160
tests/components/ecovacs/test_config_flow.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
"""Test Ecovacs config flow."""
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from sucks import EcoVacsAPI
|
||||
|
||||
from homeassistant.components.ecovacs.const import CONF_CONTINENT, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
_USER_INPUT = {
|
||||
CONF_USERNAME: "username",
|
||||
CONF_PASSWORD: "password",
|
||||
CONF_COUNTRY: "it",
|
||||
CONF_CONTINENT: "eu",
|
||||
}
|
||||
|
||||
|
||||
async def _test_user_flow(hass: HomeAssistant) -> dict[str, Any]:
|
||||
"""Test config flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
return await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=_USER_INPUT,
|
||||
)
|
||||
|
||||
|
||||
async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||
"""Test the user config flow."""
|
||||
with patch(
|
||||
"homeassistant.components.ecovacs.config_flow.EcoVacsAPI",
|
||||
return_value=Mock(spec_set=EcoVacsAPI),
|
||||
) as mock_ecovacs:
|
||||
result = await _test_user_flow(hass)
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == _USER_INPUT[CONF_USERNAME]
|
||||
assert result["data"] == _USER_INPUT
|
||||
mock_setup_entry.assert_called()
|
||||
mock_ecovacs.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "reason"),
|
||||
[
|
||||
(ValueError, "invalid_auth"),
|
||||
(Exception, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_user_flow_error(
|
||||
hass: HomeAssistant,
|
||||
side_effect: Exception,
|
||||
reason: str,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test handling invalid connection."""
|
||||
with patch(
|
||||
"homeassistant.components.ecovacs.config_flow.EcoVacsAPI",
|
||||
return_value=Mock(spec_set=EcoVacsAPI),
|
||||
) as mock_ecovacs:
|
||||
mock_ecovacs.side_effect = side_effect
|
||||
|
||||
result = await _test_user_flow(hass)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": reason}
|
||||
mock_ecovacs.assert_called()
|
||||
mock_setup_entry.assert_not_called()
|
||||
|
||||
mock_ecovacs.reset_mock(side_effect=True)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=_USER_INPUT,
|
||||
)
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == _USER_INPUT[CONF_USERNAME]
|
||||
assert result["data"] == _USER_INPUT
|
||||
mock_setup_entry.assert_called()
|
||||
|
||||
|
||||
async def test_import_flow(
|
||||
hass: HomeAssistant, issue_registry: ir.IssueRegistry, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test importing yaml config."""
|
||||
with patch(
|
||||
"homeassistant.components.ecovacs.config_flow.EcoVacsAPI",
|
||||
return_value=Mock(spec_set=EcoVacsAPI),
|
||||
) as mock_ecovacs:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=_USER_INPUT,
|
||||
)
|
||||
mock_ecovacs.assert_called()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == _USER_INPUT[CONF_USERNAME]
|
||||
assert result["data"] == _USER_INPUT
|
||||
assert (HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}") in issue_registry.issues
|
||||
mock_setup_entry.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=_USER_INPUT)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=_USER_INPUT,
|
||||
)
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert (HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}") in issue_registry.issues
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "reason"),
|
||||
[
|
||||
(ValueError, "invalid_auth"),
|
||||
(Exception, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_import_flow_error(
|
||||
hass: HomeAssistant,
|
||||
side_effect: Exception,
|
||||
reason: str,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test handling invalid connection."""
|
||||
with patch(
|
||||
"homeassistant.components.ecovacs.config_flow.EcoVacsAPI",
|
||||
return_value=Mock(spec_set=EcoVacsAPI),
|
||||
) as mock_ecovacs:
|
||||
mock_ecovacs.side_effect = side_effect
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=_USER_INPUT,
|
||||
)
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == reason
|
||||
assert (
|
||||
DOMAIN,
|
||||
f"deprecated_yaml_import_issue_{reason}",
|
||||
) in issue_registry.issues
|
||||
mock_ecovacs.assert_called()
|
Loading…
Add table
Reference in a new issue