Add config flow to enigma2 (#106348)

* add config flow to enigma2

* Apply suggestions from code review

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* fix suggested change

* use parametrize for config flow tests

* Restore PLATFORM_SCHEMA and add create_issue to async_setup_platform

* fix docstring

* remove name, refactor config flow

* bump dependency

* remove name, add verify_ssl, use async_create_clientsession

* use translation key, change integration type to device

* Bump openwebifpy to 4.2.1

* cleanup, remove CONF_NAME from entity, add async_set_unique_id

* clear unneeded constants, fix tests

* fix tests

* move _attr_translation_key out of init

* update test requirement

* Address review comments

* address review comments

* clear strings.json

* Review coments

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Sid 2024-04-17 15:21:54 +02:00 committed by GitHub
parent 764a0f29cc
commit be0926b7b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 565 additions and 40 deletions

View file

@ -389,6 +389,7 @@ build.json @home-assistant/supervisor
/homeassistant/components/energyzero/ @klaasnicolaas /homeassistant/components/energyzero/ @klaasnicolaas
/tests/components/energyzero/ @klaasnicolaas /tests/components/energyzero/ @klaasnicolaas
/homeassistant/components/enigma2/ @autinerd /homeassistant/components/enigma2/ @autinerd
/tests/components/enigma2/ @autinerd
/homeassistant/components/enocean/ @bdurrer /homeassistant/components/enocean/ @bdurrer
/tests/components/enocean/ @bdurrer /tests/components/enocean/ @bdurrer
/homeassistant/components/enphase_envoy/ @bdraco @cgarwood @dgomes @joostlek @catsmanac /homeassistant/components/enphase_envoy/ @bdraco @cgarwood @dgomes @joostlek @catsmanac

View file

@ -1 +1,48 @@
"""Support for Enigma2 devices.""" """Support for Enigma2 devices."""
from openwebif.api import OpenWebIfDevice
from yarl import URL
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from .const import DOMAIN
PLATFORMS = [Platform.MEDIA_PLAYER]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Enigma2 from a config entry."""
base_url = URL.build(
scheme="http" if not entry.data[CONF_SSL] else "https",
host=entry.data[CONF_HOST],
port=entry.data[CONF_PORT],
user=entry.data.get(CONF_USERNAME),
password=entry.data.get(CONF_PASSWORD),
)
session = async_create_clientsession(
hass, verify_ssl=entry.data[CONF_VERIFY_SSL], base_url=base_url
)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = OpenWebIfDevice(session)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View file

@ -0,0 +1,158 @@
"""Config flow for Enigma2."""
from typing import Any
from aiohttp.client_exceptions import ClientError
from openwebif.api import OpenWebIfDevice
from openwebif.error import InvalidAuthError
import voluptuous as vol
from yarl import URL
from homeassistant.components.homeassistant import DOMAIN as HOMEASSISTANT_DOMAIN
from homeassistant.config_entries import SOURCE_USER, ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.helpers import selector
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from .const import (
CONF_DEEP_STANDBY,
CONF_SOURCE_BOUQUET,
CONF_USE_CHANNEL_ICON,
DEFAULT_PORT,
DEFAULT_SSL,
DEFAULT_VERIFY_SSL,
DOMAIN,
)
CONFIG_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): selector.TextSelector(),
vol.Required(CONF_PORT, default=DEFAULT_PORT): vol.All(
selector.NumberSelector(
selector.NumberSelectorConfig(
min=1, max=65535, mode=selector.NumberSelectorMode.BOX
)
),
vol.Coerce(int),
),
vol.Optional(CONF_USERNAME): selector.TextSelector(),
vol.Optional(CONF_PASSWORD): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD)
),
vol.Required(CONF_SSL, default=DEFAULT_SSL): selector.BooleanSelector(),
vol.Required(
CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL
): selector.BooleanSelector(),
}
)
class Enigma2ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Enigma2."""
DATA_KEYS = (
CONF_HOST,
CONF_PORT,
CONF_USERNAME,
CONF_PASSWORD,
CONF_SSL,
CONF_VERIFY_SSL,
)
OPTIONS_KEYS = (CONF_DEEP_STANDBY, CONF_SOURCE_BOUQUET, CONF_USE_CHANNEL_ICON)
def __init__(self) -> None:
"""Initialize the config flow."""
super().__init__()
self.errors: dict[str, str] = {}
self._data: dict[str, Any] = {}
self._options: dict[str, Any] = {}
async def validate_user_input(self, user_input: dict[str, Any]) -> dict[str, Any]:
"""Validate user input."""
self.errors = {}
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
base_url = URL.build(
scheme="http" if not user_input[CONF_SSL] else "https",
host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
user=user_input.get(CONF_USERNAME),
password=user_input.get(CONF_PASSWORD),
)
session = async_create_clientsession(
self.hass, verify_ssl=user_input[CONF_VERIFY_SSL], base_url=base_url
)
try:
about = await OpenWebIfDevice(session).get_about()
except InvalidAuthError:
self.errors["base"] = "invalid_auth"
except ClientError:
self.errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except
self.errors["base"] = "unknown"
else:
await self.async_set_unique_id(about["info"]["ifaces"][0]["mac"])
self._abort_if_unique_id_configured()
return user_input
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the user step."""
if user_input is None:
return self.async_show_form(step_id=SOURCE_USER, data_schema=CONFIG_SCHEMA)
data = await self.validate_user_input(user_input)
if "base" in self.errors:
return self.async_show_form(
step_id=SOURCE_USER, data_schema=CONFIG_SCHEMA, errors=self.errors
)
return self.async_create_entry(
data=data, title=data[CONF_HOST], options=self._options
)
async def async_step_import(self, user_input: dict[str, Any]) -> ConfigFlowResult:
"""Validate import."""
if CONF_PORT not in user_input:
user_input[CONF_PORT] = DEFAULT_PORT
if CONF_SSL not in user_input:
user_input[CONF_SSL] = DEFAULT_SSL
user_input[CONF_VERIFY_SSL] = DEFAULT_VERIFY_SSL
async_create_issue(
self.hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_yaml_{DOMAIN}",
breaks_in_ha_version="2024.11.0",
is_fixable=False,
is_persistent=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Enigma2",
},
)
self._data = {
key: user_input[key] for key in user_input if key in self.DATA_KEYS
}
self._options = {
key: user_input[key] for key in user_input if key in self.OPTIONS_KEYS
}
return await self.async_step_user(self._data)

View file

@ -16,3 +16,4 @@ DEFAULT_PASSWORD = "dreambox"
DEFAULT_DEEP_STANDBY = False DEFAULT_DEEP_STANDBY = False
DEFAULT_SOURCE_BOUQUET = "" DEFAULT_SOURCE_BOUQUET = ""
DEFAULT_MAC_ADDRESS = "" DEFAULT_MAC_ADDRESS = ""
DEFAULT_VERIFY_SSL = False

View file

@ -2,7 +2,9 @@
"domain": "enigma2", "domain": "enigma2",
"name": "Enigma2 (OpenWebif)", "name": "Enigma2 (OpenWebif)",
"codeowners": ["@autinerd"], "codeowners": ["@autinerd"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/enigma2", "documentation": "https://www.home-assistant.io/integrations/enigma2",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["openwebif"], "loggers": ["openwebif"],
"requirements": ["openwebifpy==4.2.4"] "requirements": ["openwebifpy==4.2.4"]

View file

@ -9,7 +9,6 @@ from aiohttp.client_exceptions import ClientConnectorError, ServerDisconnectedEr
from openwebif.api import OpenWebIfDevice from openwebif.api import OpenWebIfDevice
from openwebif.enums import PowerState, RemoteControlCodes, SetVolumeOption from openwebif.enums import PowerState, RemoteControlCodes, SetVolumeOption
import voluptuous as vol import voluptuous as vol
from yarl import URL
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
MediaPlayerEntity, MediaPlayerEntity,
@ -17,6 +16,7 @@ from homeassistant.components.media_player import (
MediaPlayerState, MediaPlayerState,
MediaType, MediaType,
) )
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_NAME, CONF_NAME,
@ -26,10 +26,9 @@ from homeassistant.const import (
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_create_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@ -47,6 +46,7 @@ from .const import (
DEFAULT_SSL, DEFAULT_SSL,
DEFAULT_USE_CHANNEL_ICON, DEFAULT_USE_CHANNEL_ICON,
DEFAULT_USERNAME, DEFAULT_USERNAME,
DOMAIN,
) )
ATTR_MEDIA_CURRENTLY_RECORDING = "media_currently_recording" ATTR_MEDIA_CURRENTLY_RECORDING = "media_currently_recording"
@ -81,49 +81,44 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up of an enigma2 media player.""" """Set up of an enigma2 media player."""
if discovery_info:
# Discovery gives us the streaming service port (8001)
# which is not useful as OpenWebif never runs on that port.
# So use the default port instead.
config[CONF_PORT] = DEFAULT_PORT
config[CONF_NAME] = discovery_info["hostname"]
config[CONF_HOST] = discovery_info["host"]
config[CONF_USERNAME] = DEFAULT_USERNAME
config[CONF_PASSWORD] = DEFAULT_PASSWORD
config[CONF_SSL] = DEFAULT_SSL
config[CONF_USE_CHANNEL_ICON] = DEFAULT_USE_CHANNEL_ICON
config[CONF_MAC_ADDRESS] = DEFAULT_MAC_ADDRESS
config[CONF_DEEP_STANDBY] = DEFAULT_DEEP_STANDBY
config[CONF_SOURCE_BOUQUET] = DEFAULT_SOURCE_BOUQUET
base_url = URL.build( entry_data = {
scheme="https" if config[CONF_SSL] else "http", CONF_HOST: config[CONF_HOST],
host=config[CONF_HOST], CONF_PORT: config[CONF_PORT],
port=config.get(CONF_PORT), CONF_USERNAME: config[CONF_USERNAME],
user=config.get(CONF_USERNAME), CONF_PASSWORD: config[CONF_PASSWORD],
password=config.get(CONF_PASSWORD), CONF_SSL: config[CONF_SSL],
CONF_USE_CHANNEL_ICON: config[CONF_USE_CHANNEL_ICON],
CONF_DEEP_STANDBY: config[CONF_DEEP_STANDBY],
CONF_SOURCE_BOUQUET: config[CONF_SOURCE_BOUQUET],
}
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=entry_data
)
) )
session = async_create_clientsession(hass, verify_ssl=False, base_url=base_url)
device = OpenWebIfDevice( async def async_setup_entry(
host=session, hass: HomeAssistant,
turn_off_to_deep=config.get(CONF_DEEP_STANDBY, False), entry: ConfigEntry,
source_bouquet=config.get(CONF_SOURCE_BOUQUET), async_add_entities: AddEntitiesCallback,
) ) -> None:
"""Set up the Enigma2 media player platform."""
try: device: OpenWebIfDevice = hass.data[DOMAIN][entry.entry_id]
about = await device.get_about() about = await device.get_about()
except ClientConnectorError as err: device.mac_address = about["info"]["ifaces"][0]["mac"]
raise PlatformNotReady from err entity = Enigma2Device(entry, device, about)
async_add_entities([entity])
async_add_entities([Enigma2Device(config[CONF_NAME], device, about)])
class Enigma2Device(MediaPlayerEntity): class Enigma2Device(MediaPlayerEntity):
"""Representation of an Enigma2 box.""" """Representation of an Enigma2 box."""
_attr_has_entity_name = True _attr_has_entity_name = True
_attr_name = None
_attr_media_content_type = MediaType.TVSHOW _attr_media_content_type = MediaType.TVSHOW
_attr_supported_features = ( _attr_supported_features = (
@ -139,14 +134,23 @@ class Enigma2Device(MediaPlayerEntity):
| MediaPlayerEntityFeature.SELECT_SOURCE | MediaPlayerEntityFeature.SELECT_SOURCE
) )
def __init__(self, name: str, device: OpenWebIfDevice, about: dict) -> None: def __init__(
self, entry: ConfigEntry, device: OpenWebIfDevice, about: dict
) -> None:
"""Initialize the Enigma2 device.""" """Initialize the Enigma2 device."""
self._device: OpenWebIfDevice = device self._device: OpenWebIfDevice = device
self._device.mac_address = about["info"]["ifaces"][0]["mac"] self._entry = entry
self._attr_name = name
self._attr_unique_id = device.mac_address self._attr_unique_id = device.mac_address
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device.mac_address)},
manufacturer=about["info"]["brand"],
model=about["info"]["model"],
configuration_url=device.base,
name=entry.data[CONF_HOST],
)
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off media player.""" """Turn off media player."""
if self._device.turn_off_to_deep: if self._device.turn_off_to_deep:

View file

@ -0,0 +1,30 @@
{
"config": {
"flow_title": "{name}",
"step": {
"user": {
"description": "Please enter the connection details of your device.",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]",
"ssl": "[%key:common::config_flow::data::ssl%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"name": "[%key:common::config_flow::data::name%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
}
}

View file

@ -148,6 +148,7 @@ FLOWS = {
"emulated_roku", "emulated_roku",
"energenie_power_sockets", "energenie_power_sockets",
"energyzero", "energyzero",
"enigma2",
"enocean", "enocean",
"enphase_envoy", "enphase_envoy",
"environment_canada", "environment_canada",

View file

@ -1604,8 +1604,8 @@
}, },
"enigma2": { "enigma2": {
"name": "Enigma2 (OpenWebif)", "name": "Enigma2 (OpenWebif)",
"integration_type": "hub", "integration_type": "device",
"config_flow": false, "config_flow": true,
"iot_class": "local_polling" "iot_class": "local_polling"
}, },
"enmax": { "enmax": {

View file

@ -1173,6 +1173,9 @@ openerz-api==0.3.0
# homeassistant.components.openhome # homeassistant.components.openhome
openhomedevice==2.2.0 openhomedevice==2.2.0
# homeassistant.components.enigma2
openwebifpy==4.2.4
# homeassistant.components.opower # homeassistant.components.opower
opower==0.4.3 opower==0.4.3

View file

@ -0,0 +1 @@
"""Tests for the Enigma2 integration."""

View file

@ -0,0 +1,90 @@
"""Test the Enigma2 config flow."""
from homeassistant.components.enigma2.const import (
CONF_DEEP_STANDBY,
CONF_MAC_ADDRESS,
CONF_SOURCE_BOUQUET,
CONF_USE_CHANNEL_ICON,
DEFAULT_DEEP_STANDBY,
DEFAULT_PORT,
DEFAULT_SSL,
DEFAULT_VERIFY_SSL,
)
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
MAC_ADDRESS = "12:34:56:78:90:ab"
TEST_REQUIRED = {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_PORT,
CONF_SSL: DEFAULT_SSL,
CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL,
}
TEST_FULL = {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_PORT,
CONF_SSL: DEFAULT_SSL,
CONF_USERNAME: "root",
CONF_PASSWORD: "password",
CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL,
}
TEST_IMPORT_FULL = {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_PORT,
CONF_SSL: DEFAULT_SSL,
CONF_USERNAME: "root",
CONF_PASSWORD: "password",
CONF_NAME: "My Player",
CONF_DEEP_STANDBY: DEFAULT_DEEP_STANDBY,
CONF_SOURCE_BOUQUET: "Favourites",
CONF_MAC_ADDRESS: MAC_ADDRESS,
CONF_USE_CHANNEL_ICON: False,
}
TEST_IMPORT_REQUIRED = {CONF_HOST: "1.1.1.1"}
EXPECTED_OPTIONS = {
CONF_DEEP_STANDBY: DEFAULT_DEEP_STANDBY,
CONF_SOURCE_BOUQUET: "Favourites",
CONF_USE_CHANNEL_ICON: False,
}
class MockDevice:
"""A mock Enigma2 device."""
mac_address: str | None = "12:34:56:78:90:ab"
_base = "http://1.1.1.1"
async def _call_api(self, url: str) -> dict:
if url.endswith("/api/about"):
return {
"info": {
"ifaces": [
{
"mac": self.mac_address,
}
]
}
}
def get_version(self):
"""Return the version."""
return None
async def get_about(self) -> dict:
"""Get mock about endpoint."""
return await self._call_api("/api/about")
async def close(self):
"""Mock close."""

View file

@ -0,0 +1,149 @@
"""Test the Enigma2 config flow."""
from typing import Any
from unittest.mock import patch
from aiohttp.client_exceptions import ClientError
from openwebif.error import InvalidAuthError
import pytest
from homeassistant import config_entries
from homeassistant.components.enigma2.const import DOMAIN
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .conftest import (
EXPECTED_OPTIONS,
TEST_FULL,
TEST_IMPORT_FULL,
TEST_IMPORT_REQUIRED,
TEST_REQUIRED,
MockDevice,
)
@pytest.fixture
async def user_flow(hass: HomeAssistant) -> str:
"""Return a user-initiated flow after filling in host info."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["errors"] is None
return result["flow_id"]
@pytest.mark.parametrize(
("test_config"),
[(TEST_FULL), (TEST_REQUIRED)],
)
async def test_form_user(
hass: HomeAssistant, user_flow: str, test_config: dict[str, Any]
):
"""Test a successful user initiated flow."""
with (
patch(
"openwebif.api.OpenWebIfDevice.__new__",
return_value=MockDevice(),
),
patch(
"homeassistant.components.enigma2.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_configure(user_flow, test_config)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == test_config[CONF_HOST]
assert result["data"] == test_config
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("exception", "error_type"),
[
(InvalidAuthError, "invalid_auth"),
(ClientError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_form_user_errors(
hass: HomeAssistant, user_flow, exception: Exception, error_type: str
) -> None:
"""Test we handle errors."""
with patch(
"homeassistant.components.enigma2.config_flow.OpenWebIfDevice.__new__",
side_effect=exception,
):
result = await hass.config_entries.flow.async_configure(user_flow, TEST_FULL)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == config_entries.SOURCE_USER
assert result["errors"] == {"base": error_type}
@pytest.mark.parametrize(
("test_config", "expected_data", "expected_options"),
[
(TEST_IMPORT_FULL, TEST_FULL, EXPECTED_OPTIONS),
(TEST_IMPORT_REQUIRED, TEST_REQUIRED, {}),
],
)
async def test_form_import(
hass: HomeAssistant,
test_config: dict[str, Any],
expected_data: dict[str, Any],
expected_options: dict[str, Any],
) -> None:
"""Test we get the form with import source."""
with (
patch(
"homeassistant.components.enigma2.config_flow.OpenWebIfDevice.__new__",
return_value=MockDevice(),
),
patch(
"homeassistant.components.enigma2.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=test_config,
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == test_config[CONF_HOST]
assert result["data"] == expected_data
assert result["options"] == expected_options
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("exception", "error_type"),
[
(InvalidAuthError, "invalid_auth"),
(ClientError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_form_import_errors(
hass: HomeAssistant, exception: Exception, error_type: str
) -> None:
"""Test we handle errors on import."""
with patch(
"homeassistant.components.enigma2.config_flow.OpenWebIfDevice.__new__",
side_effect=exception,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=TEST_IMPORT_FULL,
)
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {"base": error_type}

View file

@ -0,0 +1,38 @@
"""Test the Enigma2 integration init."""
from unittest.mock import patch
from homeassistant.components.enigma2.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from .conftest import TEST_REQUIRED, MockDevice
from tests.common import MockConfigEntry
async def test_unload_entry(hass: HomeAssistant) -> None:
"""Test successful unload of entry."""
with (
patch(
"homeassistant.components.enigma2.OpenWebIfDevice.__new__",
return_value=MockDevice(),
),
patch(
"homeassistant.components.enigma2.media_player.async_setup_entry",
return_value=True,
),
):
entry = MockConfigEntry(domain=DOMAIN, data=TEST_REQUIRED, title="name")
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.NOT_LOADED
assert not hass.data.get(DOMAIN)