Reduce boilerplate to abort for matching config entries (#50186)

Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
J. Nick Koston 2021-05-11 15:00:12 -05:00 committed by GitHub
parent d6a202bd74
commit 34c84a6bbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 183 additions and 350 deletions

View file

@ -65,13 +65,9 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
if user_input is None: if user_input is None:
return await self._show_setup_form(user_input) return await self._show_setup_form(user_input)
entries = self._async_current_entries() self._async_abort_entries_match(
for entry in entries: {CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]}
if ( )
entry.data[CONF_HOST] == user_input[CONF_HOST]
and entry.data[CONF_PORT] == user_input[CONF_PORT]
):
return self.async_abort(reason="already_configured")
errors = {} errors = {}

View file

@ -50,8 +50,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle external yaml configuration.""" """Handle external yaml configuration."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
config = self.hass.data.get(DATA_AMBICLIMATE_IMPL, {}) config = self.hass.data.get(DATA_AMBICLIMATE_IMPL, {})
@ -63,8 +62,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_auth(self, user_input=None): async def async_step_auth(self, user_input=None):
"""Handle a flow start.""" """Handle a flow start."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
errors = {} errors = {}
@ -85,8 +83,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_code(self, code=None): async def async_step_code(self, code=None):
"""Received code for authentication.""" """Received code for authentication."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
token_info = await self._get_token_info(code) token_info = await self._get_token_info(code)

View file

@ -298,11 +298,7 @@ class BroadlinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, import_info): async def async_step_import(self, import_info):
"""Import a device.""" """Import a device."""
if any( self._async_abort_entries_match({CONF_HOST: import_info[CONF_HOST]})
import_info[CONF_HOST] == entry.data[CONF_HOST]
for entry in self._async_current_entries()
):
return self.async_abort(reason="already_configured")
return await self.async_step_user(import_info) return await self.async_step_user(import_info)
async def async_step_reauth(self, data): async def async_step_reauth(self, data):

View file

@ -199,9 +199,7 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"unique_id's will not be available", "unique_id's will not be available",
self.host, self.host,
) )
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: self.host})
if entry.data[CONF_HOST] == self.host:
return self.async_abort(reason="already_configured")
return self.async_create_entry( return self.async_create_entry(
title=receiver.name, title=receiver.name,

View file

@ -73,8 +73,7 @@ class DuneHDConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle configuration by yaml file.""" """Handle configuration by yaml file."""
self.host = user_input[CONF_HOST] self.host = user_input[CONF_HOST]
if self.host_already_configured(self.host): self._async_abort_entries_match({CONF_HOST: self.host})
return self.async_abort(reason="already_configured")
try: try:
await self.init_device(self.host) await self.init_device(self.host)

View file

@ -26,12 +26,8 @@ class EmulatedRokuFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input is not None: if user_input is not None:
name = user_input[CONF_NAME] self._async_abort_entries_match({CONF_NAME: user_input[CONF_NAME]})
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
if name in configured_servers(self.hass):
return self.async_abort(reason="already_configured")
return self.async_create_entry(title=name, data=user_input)
servers_num = len(configured_servers(self.hass)) servers_num = len(configured_servers(self.hass))

View file

@ -133,9 +133,7 @@ class ForkedDaapdFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
""" """
if user_input is not None: if user_input is not None:
# check for any entries with same host, abort if found # check for any entries with same host, abort if found
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
if entry.data.get(CONF_HOST) == user_input[CONF_HOST]:
return self.async_abort(reason="already_configured")
validate_result = await self.validate_input(user_input) validate_result = await self.validate_input(user_input)
if validate_result[0] == "ok": # success if validate_result[0] == "ok": # success
_LOGGER.debug("Connected successfully. Creating entry") _LOGGER.debug("Connected successfully. Creating entry")

View file

@ -47,13 +47,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
Data has the keys from DATA_SCHEMA with values provided by the user. Data has the keys from DATA_SCHEMA with values provided by the user.
""" """
self._async_abort_entries_match(
for entry in self.hass.config_entries.async_entries(DOMAIN): {CONF_HOST: data[CONF_HOST], CONF_PORT: data[CONF_PORT]}
if ( )
entry.data[CONF_HOST] == data[CONF_HOST]
and entry.data[CONF_PORT] == data[CONF_PORT]
):
raise AbortFlow("already_configured")
camera = FoscamCamera( camera = FoscamCamera(
data[CONF_HOST], data[CONF_HOST],

View file

@ -92,10 +92,7 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input is not None: if user_input is not None:
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
for entry in self.hass.config_entries.async_entries(DOMAIN):
if entry.data[CONF_HOST] == user_input[CONF_HOST]:
return self.async_abort(reason="already_configured")
self._host = user_input[CONF_HOST] self._host = user_input[CONF_HOST]
self._name = user_input[CONF_HOST] self._name = user_input[CONF_HOST]

View file

@ -28,8 +28,7 @@ class GoalZeroFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
host = user_input[CONF_HOST] host = user_input[CONF_HOST]
name = user_input[CONF_NAME] name = user_input[CONF_NAME]
if await self._async_endpoint_existed(host): self._async_abort_entries_match({CONF_HOST: host})
return self.async_abort(reason="already_configured")
try: try:
await self._async_try_connect(host) await self._async_try_connect(host)
@ -64,12 +63,6 @@ class GoalZeroFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors, errors=errors,
) )
async def _async_endpoint_existed(self, endpoint):
for entry in self._async_current_entries():
if endpoint == entry.data.get(CONF_HOST):
return True
return False
async def _async_try_connect(self, host): async def _async_try_connect(self, host):
session = async_get_clientsession(self.hass) session = async_get_clientsession(self.hass)
api = Yeti(host, self.hass.loop, session) api = Yeti(host, self.hass.loop, session)

View file

@ -35,9 +35,7 @@ class Gogogate2FlowHandler(ConfigFlow, domain=DOMAIN):
ip_address = discovery_info["host"] ip_address = discovery_info["host"]
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_IP_ADDRESS: ip_address})
if entry.data.get(CONF_IP_ADDRESS) == ip_address:
return self.async_abort(reason="already_configured")
self._ip_address = ip_address self._ip_address = ip_address
self._device_type = DEVICE_TYPE_ISMARTGATE self._device_type = DEVICE_TYPE_ISMARTGATE

View file

@ -6,7 +6,6 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import callback
from .const import ( from .const import (
CONF_2FA, CONF_2FA,
@ -22,15 +21,6 @@ from .hangups_utils import (
) )
@callback
def configured_hangouts(hass):
"""Return the configures Google Hangouts Account."""
entries = hass.config_entries.async_entries(HANGOUTS_DOMAIN)
if entries:
return entries[0]
return None
@config_entries.HANDLERS.register(HANGOUTS_DOMAIN) @config_entries.HANDLERS.register(HANGOUTS_DOMAIN)
class HangoutsFlowHandler(config_entries.ConfigFlow): class HangoutsFlowHandler(config_entries.ConfigFlow):
"""Config flow Google Hangouts.""" """Config flow Google Hangouts."""
@ -46,8 +36,7 @@ class HangoutsFlowHandler(config_entries.ConfigFlow):
"""Handle a flow start.""" """Handle a flow start."""
errors = {} errors = {}
if configured_hangouts(self.hass) is not None: self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
if user_input is not None: if user_input is not None:
user_email = user_input[CONF_EMAIL] user_email = user_input[CONF_EMAIL]

View file

@ -85,8 +85,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION]) parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])
friendly_name = discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME] friendly_name = discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME]
if self._host_already_configured(parsed_url.hostname): self._async_abort_entries_match({CONF_HOST: parsed_url.hostname})
return self.async_abort(reason="already_configured")
self.context["title_placeholders"] = {"name": friendly_name} self.context["title_placeholders"] = {"name": friendly_name}
@ -147,16 +146,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=validated[CONF_NAME], data=data) return self.async_create_entry(title=validated[CONF_NAME], data=data)
def _host_already_configured(self, host):
"""See if we already have a harmony entry matching the host."""
for entry in self._async_current_entries():
if CONF_HOST not in entry.data:
continue
if entry.data[CONF_HOST] == host:
return True
return False
def _options_from_user_input(user_input): def _options_from_user_input(user_input):
options = {} options = {}

View file

@ -125,12 +125,7 @@ class HueFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
data_schema=vol.Schema({vol.Required(CONF_HOST): str}), data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
) )
if any( self._async_abort_entries_match({"host": user_input["host"]})
user_input["host"] == entry.data.get("host")
for entry in self._async_current_entries()
):
return self.async_abort(reason="already_configured")
self.bridge = self._async_get_bridge(user_input[CONF_HOST]) self.bridge = self._async_get_bridge(user_input[CONF_HOST])
return await self.async_step_link() return await self.async_step_link()
@ -233,11 +228,7 @@ class HueFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
This flow is also triggered by `async_step_discovery`. This flow is also triggered by `async_step_discovery`.
""" """
# Check if host exists, abort if so. # Check if host exists, abort if so.
if any( self._async_abort_entries_match({"host": import_info["host"]})
import_info["host"] == entry.data.get("host")
for entry in self._async_current_entries()
):
return self.async_abort(reason="already_configured")
self.bridge = self._async_get_bridge(import_info["host"]) self.bridge = self._async_get_bridge(import_info["host"])
return await self.async_step_link() return await self.async_step_link()

View file

@ -7,7 +7,7 @@ from aiopvapi.helpers.aiorequest import AioRequest
import async_timeout import async_timeout
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, core, data_entry_flow, exceptions from homeassistant import config_entries, core, exceptions
from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS
from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -73,8 +73,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) )
async def _async_validate_or_error(self, host): async def _async_validate_or_error(self, host):
if self._host_already_configured(host): self._async_abort_entries_match({CONF_HOST: host})
raise data_entry_flow.AbortFlow("already_configured")
try: try:
info = await validate_input(self.hass, host) info = await validate_input(self.hass, host)
@ -118,8 +117,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if progress.get("context", {}).get(CONF_HOST) == self.discovered_ip: if progress.get("context", {}).get(CONF_HOST) == self.discovered_ip:
return self.async_abort(reason="already_in_progress") return self.async_abort(reason="already_in_progress")
if self._host_already_configured(self.discovered_ip): self._async_abort_entries_match({CONF_HOST: self.discovered_ip})
return self.async_abort(reason="already_configured")
info, error = await self._async_validate_or_error(self.discovered_ip) info, error = await self._async_validate_or_error(self.discovered_ip)
if error: if error:
@ -148,15 +146,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
step_id="link", description_placeholders=self.powerview_config step_id="link", description_placeholders=self.powerview_config
) )
def _host_already_configured(self, host):
"""See if we already have a hub with the host address configured."""
existing_hosts = {
entry.data.get(CONF_HOST)
for entry in self._async_current_entries()
if CONF_HOST in entry.data
}
return host in existing_hosts
class CannotConnect(exceptions.HomeAssistantError): class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect.""" """Error to indicate we cannot connect."""

View file

@ -47,9 +47,7 @@ class KeeneticFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a flow initialized by the user.""" """Handle a flow initialized by the user."""
errors = {} errors = {}
if user_input is not None: if user_input is not None:
for entry in self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
if entry.data[CONF_HOST] == user_input[CONF_HOST]:
return self.async_abort(reason="already_configured")
_client = Client( _client = Client(
TelnetConnection( TelnetConnection(

View file

@ -8,7 +8,7 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_BASE, CONF_HOST, CONF_PASSWORD from homeassistant.const import CONF_BASE, CONF_HOST, CONF_PASSWORD
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
@ -23,14 +23,6 @@ DATA_SCHEMA = vol.Schema(
) )
@callback
def configured_instances(hass):
"""Return a set of configured Kostal Plenticore HOSTS."""
return {
entry.data[CONF_HOST] for entry in hass.config_entries.async_entries(DOMAIN)
}
async def test_connection(hass: HomeAssistant, data) -> str: async def test_connection(hass: HomeAssistant, data) -> str:
"""Test the connection to the inverter. """Test the connection to the inverter.
@ -56,8 +48,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
hostname = None hostname = None
if user_input is not None: if user_input is not None:
if user_input[CONF_HOST] in configured_instances(self.hass): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
return self.async_abort(reason="already_configured")
try: try:
hostname = await test_connection(self.hass, user_input) hostname = await test_connection(self.hass, user_input)
except PlenticoreAuthenticationException as ex: except PlenticoreAuthenticationException as ex:

View file

@ -27,9 +27,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input is not None: if user_input is not None:
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]})
if entry.data[CONF_USERNAME] == user_input[CONF_USERNAME]:
return self.async_abort(reason="already_configured")
hub = LitterRobotHub(self.hass, user_input) hub = LitterRobotHub(self.hass, user_input)
try: try:

View file

@ -65,8 +65,7 @@ class LogiCircleFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, user_input=None): async def async_step_import(self, user_input=None):
"""Handle external yaml configuration.""" """Handle external yaml configuration."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
self.flow_impl = DOMAIN self.flow_impl = DOMAIN
@ -76,8 +75,7 @@ class LogiCircleFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a flow start.""" """Handle a flow start."""
flows = self.hass.data.get(DATA_FLOW_IMPL, {}) flows = self.hass.data.get(DATA_FLOW_IMPL, {})
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
if not flows: if not flows:
return self.async_abort(reason="missing_configuration") return self.async_abort(reason="missing_configuration")
@ -138,8 +136,7 @@ class LogiCircleFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_code(self, code=None): async def async_step_code(self, code=None):
"""Received code for authentication.""" """Received code for authentication."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
return await self._async_create_session(code) return await self._async_create_session(code)

View file

@ -14,7 +14,6 @@ from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import callback from homeassistant.core import callback
from .const import ( from .const import (
ABORT_REASON_ALREADY_CONFIGURED,
ABORT_REASON_CANNOT_CONNECT, ABORT_REASON_CANNOT_CONNECT,
BRIDGE_TIMEOUT, BRIDGE_TIMEOUT,
CONF_CA_CERTS, CONF_CA_CERTS,
@ -89,8 +88,7 @@ class LutronCasetaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle pairing with the hub.""" """Handle pairing with the hub."""
errors = {} errors = {}
# Abort if existing entry with matching host exists. # Abort if existing entry with matching host exists.
if self._async_data_host_is_already_configured(): self._async_abort_entries_match({CONF_HOST: self.data[CONF_HOST]})
return self.async_abort(reason=ABORT_REASON_ALREADY_CONFIGURED)
self._configure_tls_assets() self._configure_tls_assets()
@ -155,15 +153,6 @@ class LutronCasetaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
for asset_key, conf_key in FILE_MAPPING.items(): for asset_key, conf_key in FILE_MAPPING.items():
self.data[conf_key] = TLS_ASSET_TEMPLATE.format(self.bridge_id, asset_key) self.data[conf_key] = TLS_ASSET_TEMPLATE.format(self.bridge_id, asset_key)
@callback
def _async_data_host_is_already_configured(self):
"""Check to see if the host is already configured."""
return any(
self.data[CONF_HOST] == entry.data[CONF_HOST]
for entry in self._async_current_entries()
if CONF_HOST in entry.data
)
async def async_step_import(self, import_info): async def async_step_import(self, import_info):
"""Import a new Caseta bridge as a config entry. """Import a new Caseta bridge as a config entry.
@ -174,8 +163,7 @@ class LutronCasetaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self.data[CONF_HOST] = host self.data[CONF_HOST] = host
# Abort if existing entry with matching host exists. # Abort if existing entry with matching host exists.
if self._async_data_host_is_already_configured(): self._async_abort_entries_match({CONF_HOST: self.data[CONF_HOST]})
return self.async_abort(reason=ABORT_REASON_ALREADY_CONFIGURED)
self.data[CONF_KEYFILE] = import_info[CONF_KEYFILE] self.data[CONF_KEYFILE] = import_info[CONF_KEYFILE]
self.data[CONF_CERTFILE] = import_info[CONF_CERTFILE] self.data[CONF_CERTFILE] = import_info[CONF_CERTFILE]

View file

@ -9,7 +9,6 @@ CONF_CA_CERTS = "ca_certs"
STEP_IMPORT_FAILED = "import_failed" STEP_IMPORT_FAILED = "import_failed"
ERROR_CANNOT_CONNECT = "cannot_connect" ERROR_CANNOT_CONNECT = "cannot_connect"
ABORT_REASON_CANNOT_CONNECT = "cannot_connect" ABORT_REASON_CANNOT_CONNECT = "cannot_connect"
ABORT_REASON_ALREADY_CONFIGURED = "already_configured"
BRIDGE_LEAP = "leap" BRIDGE_LEAP = "leap"
BRIDGE_LIP = "lip" BRIDGE_LIP = "lip"

View file

@ -121,9 +121,7 @@ class MotionEyeConfigFlow(ConfigFlow, domain=DOMAIN):
# Search for duplicates: there isn't a useful unique_id, but # Search for duplicates: there isn't a useful unique_id, but
# at least prevent entries with the same motionEye URL. # at least prevent entries with the same motionEye URL.
for existing_entry in self._async_current_entries(include_ignore=False): self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})
if existing_entry.data.get(CONF_URL) == user_input[CONF_URL]:
return self.async_abort(reason="already_configured")
return self.async_create_entry( return self.async_create_entry(
title=f"{user_input[CONF_URL]}", title=f"{user_input[CONF_URL]}",

View file

@ -17,8 +17,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the initial step.""" """Handle the initial step."""
if self.hass.config_entries.async_entries(DOMAIN): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
errors = {} errors = {}
if user_input is not None: if user_input is not None:

View file

@ -56,20 +56,6 @@ async def validate_input_owserver(
return {"title": host} return {"title": host}
def is_duplicate_owserver_entry(
hass: HomeAssistant, user_input: dict[str, Any]
) -> bool:
"""Check existing entries for matching host and port."""
for config_entry in hass.config_entries.async_entries(DOMAIN):
if (
config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER
and config_entry.data[CONF_HOST] == user_input[CONF_HOST]
and config_entry.data[CONF_PORT] == user_input[CONF_PORT]
):
return True
return False
async def validate_input_mount_dir( async def validate_input_mount_dir(
hass: HomeAssistant, data: dict[str, Any] hass: HomeAssistant, data: dict[str, Any]
) -> dict[str, str]: ) -> dict[str, str]:
@ -125,8 +111,13 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input: if user_input:
# Prevent duplicate entries # Prevent duplicate entries
if is_duplicate_owserver_entry(self.hass, user_input): self._async_abort_entries_match(
return self.async_abort(reason="already_configured") {
CONF_TYPE: CONF_TYPE_OWSERVER,
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
}
)
self.onewire_config.update(user_input) self.onewire_config.update(user_input)

View file

@ -49,9 +49,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, conf: dict) -> dict: async def async_step_import(self, conf: dict) -> dict:
"""Import a configuration from config.yaml.""" """Import a configuration from config.yaml."""
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: conf[CONF_HOST]})
if entry.data[CONF_HOST] == conf[CONF_HOST]:
return self.async_abort(reason="already_configured")
return await self.async_step_user( return await self.async_step_user(
{ {

View file

@ -113,9 +113,7 @@ class PlugwiseConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
user_input[CONF_HOST] = self.discovery_info[CONF_HOST] user_input[CONF_HOST] = self.discovery_info[CONF_HOST]
user_input[CONF_PORT] = self.discovery_info.get(CONF_PORT, DEFAULT_PORT) user_input[CONF_PORT] = self.discovery_info.get(CONF_PORT, DEFAULT_PORT)
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
if entry.data.get(CONF_HOST) == user_input[CONF_HOST]:
return self.async_abort(reason="already_configured")
try: try:
api = await validate_gw_input(self.hass, user_input) api = await validate_gw_input(self.hass, user_input)

View file

@ -12,7 +12,6 @@ import voluptuous as vol
from homeassistant import config_entries, core, exceptions from homeassistant import config_entries, core, exceptions
from homeassistant.components.dhcp import IP_ADDRESS from homeassistant.components.dhcp import IP_ADDRESS
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
from homeassistant.core import callback
from .const import DOMAIN from .const import DOMAIN
@ -60,9 +59,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_dhcp(self, discovery_info): async def async_step_dhcp(self, discovery_info):
"""Handle dhcp discovery.""" """Handle dhcp discovery."""
if self._async_ip_address_already_configured(discovery_info[IP_ADDRESS]): self.ip_address = discovery_info[IP_ADDRESS]
return self.async_abort(reason="already_configured") self._async_abort_entries_match({CONF_IP_ADDRESS: self.ip_address})
self.ip_address = discovery_info[IP_ADDRESS] self.ip_address = discovery_info[IP_ADDRESS]
self.context["title_placeholders"] = {CONF_IP_ADDRESS: self.ip_address} self.context["title_placeholders"] = {CONF_IP_ADDRESS: self.ip_address}
return await self.async_step_user() return await self.async_step_user()
@ -111,14 +109,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self.ip_address = data[CONF_IP_ADDRESS] self.ip_address = data[CONF_IP_ADDRESS]
return await self.async_step_user() return await self.async_step_user()
@callback
def _async_ip_address_already_configured(self, ip_address):
"""See if we already have an entry matching the ip_address."""
for entry in self._async_current_entries():
if entry.data.get(CONF_IP_ADDRESS) == ip_address:
return True
return False
class WrongVersion(exceptions.HomeAssistantError): class WrongVersion(exceptions.HomeAssistantError):
"""Error to indicate the powerwall uses a software version we cannot interact with.""" """Error to indicate the powerwall uses a software version we cannot interact with."""

View file

@ -15,17 +15,6 @@ DATA_SCHEMA = vol.Schema(
async def validate_input(hass: core.HomeAssistant, data): async def validate_input(hass: core.HomeAssistant, data):
"""Validate the user host input.""" """Validate the user host input."""
confs = hass.config_entries.async_entries(DOMAIN)
same_entries = [
True
for entry in confs
if entry.data.get("host") == data["host"]
and entry.data.get("port") == data["port"]
]
if same_entries:
raise ExistingEntry
api_instance = ProgettiHWSWAPI(f'{data["host"]}:{data["port"]}') api_instance = ProgettiHWSWAPI(f'{data["host"]}:{data["port"]}')
is_valid = await api_instance.check_board() is_valid = await api_instance.check_board()
@ -80,13 +69,14 @@ class ProgettiHWSWConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step.""" """Handle the initial step."""
errors = {} errors = {}
if user_input is not None: if user_input is not None:
self._async_abort_entries_match(
{"host": user_input["host"], "port": user_input["port"]}
)
try: try:
info = await validate_input(self.hass, user_input) info = await validate_input(self.hass, user_input)
except CannotConnect: except CannotConnect:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except ExistingEntry:
return self.async_abort(reason="already_configured")
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:

View file

@ -79,14 +79,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_homekit(self, discovery_info): async def async_step_homekit(self, discovery_info):
"""Handle HomeKit discovery.""" """Handle HomeKit discovery."""
if self._async_current_entries(): self._async_abort_entries_match()
# We can see rachio on the network to tell them to configure
# it, but since the device will not give up the account it is
# bound to and there can be multiple rachio systems on a single
# account, we avoid showing the device as discovered once
# they already have one configured as they can always
# add a new one via "+"
return self.async_abort(reason="already_configured")
properties = { properties = {
key.lower(): value for (key, value) in discovery_info["properties"].items() key.lower(): value for (key, value) in discovery_info["properties"].items()
} }

View file

@ -6,7 +6,6 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.helpers.typing import DiscoveryInfoType
@ -53,14 +52,6 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Define the config flow to handle options.""" """Define the config flow to handle options."""
return RainMachineOptionsFlowHandler(config_entry) return RainMachineOptionsFlowHandler(config_entry)
@callback
def _async_abort_ip_address_configured(self, ip_address):
"""Abort if we already have an entry for the ip."""
# IP already configured
for entry in self._async_current_entries(include_ignore=False):
if ip_address == entry.data[CONF_IP_ADDRESS]:
raise AbortFlow("already_configured")
async def async_step_homekit(self, discovery_info): async def async_step_homekit(self, discovery_info):
"""Handle a flow initialized by homekit discovery.""" """Handle a flow initialized by homekit discovery."""
return await self.async_step_zeroconf(discovery_info) return await self.async_step_zeroconf(discovery_info)
@ -69,7 +60,7 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle discovery via zeroconf.""" """Handle discovery via zeroconf."""
ip_address = discovery_info["host"] ip_address = discovery_info["host"]
self._async_abort_ip_address_configured(ip_address) self._async_abort_entries_match({CONF_IP_ADDRESS: ip_address})
# Handle IP change # Handle IP change
for entry in self._async_current_entries(include_ignore=False): for entry in self._async_current_entries(include_ignore=False):
# Try our existing credentials to check for ip change # Try our existing credentials to check for ip change
@ -109,7 +100,9 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the start of the config flow.""" """Handle the start of the config flow."""
errors = {} errors = {}
if user_input: if user_input:
self._async_abort_ip_address_configured(user_input[CONF_IP_ADDRESS]) self._async_abort_entries_match(
{CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS]}
)
controller = await async_get_controller( controller = await async_get_controller(
self.hass, self.hass,
user_input[CONF_IP_ADDRESS], user_input[CONF_IP_ADDRESS],

View file

@ -88,8 +88,7 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN):
# If we already have the host configured do # If we already have the host configured do
# not open connections to it if we can avoid it. # not open connections to it if we can avoid it.
if self._host_already_configured(discovery_info[CONF_HOST]): self._async_abort_entries_match({CONF_HOST: discovery_info[CONF_HOST]})
return self.async_abort(reason="already_configured")
self.discovery_info.update({CONF_HOST: discovery_info[CONF_HOST]}) self.discovery_info.update({CONF_HOST: discovery_info[CONF_HOST]})
@ -151,12 +150,3 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN):
title=self.discovery_info[CONF_NAME], title=self.discovery_info[CONF_NAME],
data=self.discovery_info, data=self.discovery_info,
) )
def _host_already_configured(self, host):
"""See if we already have a hub with the host address configured."""
existing_hosts = {
entry.data[CONF_HOST]
for entry in self._async_current_entries()
if CONF_HOST in entry.data
}
return host in existing_hosts

View file

@ -79,8 +79,7 @@ class RoombaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_dhcp(self, discovery_info): async def async_step_dhcp(self, discovery_info):
"""Handle dhcp discovery.""" """Handle dhcp discovery."""
if self._async_host_already_configured(discovery_info[IP_ADDRESS]): self._async_abort_entries_match({CONF_HOST: discovery_info[IP_ADDRESS]})
return self.async_abort(reason="already_configured")
if not discovery_info[HOSTNAME].startswith(("irobot-", "roomba-")): if not discovery_info[HOSTNAME].startswith(("irobot-", "roomba-")):
return self.async_abort(reason="not_irobot_device") return self.async_abort(reason="not_irobot_device")
@ -183,11 +182,7 @@ class RoombaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
), ),
) )
if any( self._async_abort_entries_match({CONF_HOST: user_input["host"]})
user_input["host"] == entry.data.get("host")
for entry in self._async_current_entries()
):
return self.async_abort(reason="already_configured")
self.host = user_input[CONF_HOST] self.host = user_input[CONF_HOST]
self.blid = user_input[CONF_BLID].upper() self.blid = user_input[CONF_BLID].upper()
@ -260,14 +255,6 @@ class RoombaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors, errors=errors,
) )
@callback
def _async_host_already_configured(self, host):
"""See if we already have an entry matching the host."""
for entry in self._async_current_entries():
if entry.data.get(CONF_HOST) == host:
return True
return False
class OptionsFlowHandler(config_entries.OptionsFlow): class OptionsFlowHandler(config_entries.OptionsFlow):
"""Handle options.""" """Handle options."""

View file

@ -60,8 +60,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_dhcp(self, discovery_info): async def async_step_dhcp(self, discovery_info):
"""Handle dhcp discovery.""" """Handle dhcp discovery."""
if self._host_already_configured(discovery_info[IP_ADDRESS]): self._async_abort_entries_match({CONF_HOST: discovery_info[IP_ADDRESS]})
return self.async_abort(reason="already_configured")
formatted_mac = format_mac(discovery_info[MAC_ADDRESS]) formatted_mac = format_mac(discovery_info[MAC_ADDRESS])
await self.async_set_unique_id(format_mac(formatted_mac)) await self.async_set_unique_id(format_mac(formatted_mac))
@ -79,8 +78,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input is not None: if user_input is not None:
if self._host_already_configured(user_input[CONF_HOST]): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
return self.async_abort(reason="already_configured")
try: try:
info = await validate_input(self.hass, user_input) info = await validate_input(self.hass, user_input)
@ -108,18 +106,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, user_input): async def async_step_import(self, user_input):
"""Handle import.""" """Handle import."""
if self._host_already_configured(user_input[CONF_HOST]): self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
return self.async_abort(reason="already_configured")
return await self.async_step_user(user_input) return await self.async_step_user(user_input)
def _host_already_configured(self, host):
"""See if we already have an entry matching the host."""
for entry in self._async_current_entries():
if entry.data.get(CONF_HOST) == host:
return True
return False
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow(config_entry): def async_get_options_flow(config_entry):

View file

@ -10,7 +10,6 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import ssdp from homeassistant.components import ssdp
from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import callback
from .const import CONF_ENDPOINT, DOMAIN from .const import CONF_ENDPOINT, DOMAIN
@ -75,9 +74,7 @@ class SongpalConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_init(self, user_input=None): async def async_step_init(self, user_input=None):
"""Handle a flow start.""" """Handle a flow start."""
# Check if already configured # Check if already configured
if self._async_endpoint_already_configured(): self._async_abort_entries_match({CONF_ENDPOINT: self.conf.endpoint})
return self.async_abort(reason="already_configured")
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
step_id="init", step_id="init",
@ -144,11 +141,3 @@ class SongpalConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self.conf = SongpalConfig(name, parsed_url.hostname, endpoint) self.conf = SongpalConfig(name, parsed_url.hostname, endpoint)
return await self.async_step_init(user_input) return await self.async_step_init(user_input)
@callback
def _async_endpoint_already_configured(self):
"""See if we already have an endpoint matching user input configured."""
for entry in self._async_current_entries():
if entry.data.get(CONF_ENDPOINT) == self.conf.endpoint:
return True
return False

View file

@ -37,10 +37,7 @@ class SubaruConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
error = None error = None
if user_input: if user_input:
if user_input[CONF_USERNAME] in [ self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]})
entry.data[CONF_USERNAME] for entry in self._async_current_entries()
]:
return self.async_abort(reason="already_configured")
try: try:
await self.validate_login_creds(user_input) await self.validate_login_creds(user_input)

View file

@ -82,14 +82,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_homekit(self, discovery_info): async def async_step_homekit(self, discovery_info):
"""Handle HomeKit discovery.""" """Handle HomeKit discovery."""
if self._async_current_entries(): self._async_abort_entries_match()
# We can see tado on the network to tell them to configure
# it, but since the device will not give up the account it is
# bound to and there can be multiple tado devices on a single
# account, we avoid showing the device as discovered once
# they already have one configured as they can always
# add a new one via "+"
return self.async_abort(reason="already_configured")
properties = { properties = {
key.lower(): value for (key, value) in discovery_info["properties"].items() key.lower(): value for (key, value) in discovery_info["properties"].items()
} }

View file

@ -26,8 +26,7 @@ class TibberConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the initial step.""" """Handle the initial step."""
if self._async_current_entries(): self._async_abort_entries_match()
return self.async_abort(reason="already_configured")
if user_input is not None: if user_input is not None:
access_token = user_input[CONF_ACCESS_TOKEN].replace(" ", "") access_token = user_input[CONF_ACCESS_TOKEN].replace(" ", "")

View file

@ -106,9 +106,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, user_input): async def async_step_import(self, user_input):
"""Import a config entry.""" """Import a config entry."""
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: user_input["host"]})
if entry.data.get(CONF_HOST) == user_input["host"]:
return self.async_abort(reason="already_configured")
# Happens if user has host directly in configuration.yaml # Happens if user has host directly in configuration.yaml
if "key" not in user_input: if "key" not in user_input:

View file

@ -66,10 +66,7 @@ class TwenteMilieuFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "invalid_address" errors["base"] = "invalid_address"
return await self._show_setup_form(errors) return await self._show_setup_form(errors)
entries = self._async_current_entries() self._async_abort_entries_match({CONF_ID: unique_id})
for entry in entries:
if entry.data[CONF_ID] == unique_id:
return self.async_abort(reason="already_configured")
return self.async_create_entry( return self.async_create_entry(
title=str(unique_id), title=str(unique_id),

View file

@ -225,8 +225,7 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
CONF_HOST: parsed_url.hostname, CONF_HOST: parsed_url.hostname,
} }
if self._host_already_configured(self.config[CONF_HOST]): self._async_abort_entries_match({CONF_HOST: self.config[CONF_HOST]})
return self.async_abort(reason="already_configured")
await self.async_set_unique_id(mac_address) await self.async_set_unique_id(mac_address)
self._abort_if_unique_id_configured(updates=self.config) self._abort_if_unique_id_configured(updates=self.config)
@ -242,13 +241,6 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
return await self.async_step_user() return await self.async_step_user()
def _host_already_configured(self, host):
"""See if we already have a UniFi entry matching the host."""
for entry in self._async_current_entries():
if entry.data.get(CONF_HOST) == host:
return True
return False
class UnifiOptionsFlowHandler(config_entries.OptionsFlow): class UnifiOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle Unifi options.""" """Handle Unifi options."""

View file

@ -52,8 +52,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) )
except CannotConnect: except CannotConnect:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except AlreadyConfigured:
return self.async_abort(reason="already_configured")
else: else:
return await self.async_step_pick_device() return await self.async_step_pick_device()
@ -114,8 +112,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
except CannotConnect: except CannotConnect:
_LOGGER.error("Failed to import %s: cannot connect", host) _LOGGER.error("Failed to import %s: cannot connect", host)
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")
except AlreadyConfigured:
return self.async_abort(reason="already_configured")
if CONF_NIGHTLIGHT_SWITCH_TYPE in user_input: if CONF_NIGHTLIGHT_SWITCH_TYPE in user_input:
user_input[CONF_NIGHTLIGHT_SWITCH] = ( user_input[CONF_NIGHTLIGHT_SWITCH] = (
user_input.pop(CONF_NIGHTLIGHT_SWITCH_TYPE) user_input.pop(CONF_NIGHTLIGHT_SWITCH_TYPE)
@ -125,9 +121,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def _async_try_connect(self, host): async def _async_try_connect(self, host):
"""Set up with options.""" """Set up with options."""
for entry in self._async_current_entries(): self._async_abort_entries_match({CONF_HOST: host})
if entry.data.get(CONF_HOST) == host:
raise AlreadyConfigured
bulb = yeelight.Bulb(host) bulb = yeelight.Bulb(host)
try: try:
@ -195,7 +189,3 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
class CannotConnect(exceptions.HomeAssistantError): class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect.""" """Error to indicate we cannot connect."""
class AlreadyConfigured(exceptions.HomeAssistantError):
"""Indicate the ip address is already configured."""

View file

@ -1085,6 +1085,17 @@ class ConfigFlow(data_entry_flow.FlowHandler):
"""Get the options flow for this handler.""" """Get the options flow for this handler."""
raise data_entry_flow.UnknownHandler raise data_entry_flow.UnknownHandler
@callback
def _async_abort_entries_match(
self, match_dict: dict[str, Any] | None = None
) -> None:
"""Abort if current entries match all data."""
if match_dict is None:
match_dict = {} # Match any entry
for entry in self._async_current_entries(include_ignore=False):
if all(item in entry.data.items() for item in match_dict.items()):
raise data_entry_flow.AbortFlow("already_configured")
@callback @callback
def _abort_if_unique_id_configured( def _abort_if_unique_id_configured(
self, self,

View file

@ -2,14 +2,17 @@
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import ambiclimate import ambiclimate
import pytest
from homeassistant import data_entry_flow from homeassistant import config_entries, data_entry_flow
from homeassistant.components.ambiclimate import config_flow from homeassistant.components.ambiclimate import config_flow
from homeassistant.config import async_process_ha_core_config from homeassistant.config import async_process_ha_core_config
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util import aiohttp from homeassistant.util import aiohttp
from tests.common import MockConfigEntry
async def init_config_flow(hass): async def init_config_flow(hass):
"""Init a configuration flow.""" """Init a configuration flow."""
@ -40,12 +43,15 @@ async def test_abort_if_already_setup(hass):
"""Test we abort if Ambiclimate is already setup.""" """Test we abort if Ambiclimate is already setup."""
flow = await init_config_flow(hass) flow = await init_config_flow(hass)
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): MockConfigEntry(domain=config_flow.DOMAIN).add_to_hass(hass)
result = await flow.async_step_user() result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
context={"source": config_entries.SOURCE_USER},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): with pytest.raises(data_entry_flow.AbortFlow):
result = await flow.async_step_code() result = await flow.async_step_code()
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
@ -103,11 +109,11 @@ async def test_abort_invalid_code(hass):
async def test_already_setup(hass): async def test_already_setup(hass):
"""Test when already setup.""" """Test when already setup."""
config_flow.register_flow_implementation(hass, None, None) MockConfigEntry(domain=config_flow.DOMAIN).add_to_hass(hass)
flow = await init_config_flow(hass) result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
with patch.object(hass.config_entries, "async_entries", return_value=True): context={"source": config_entries.SOURCE_USER},
result = await flow.async_step_user() )
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"

View file

@ -1,4 +1,5 @@
"""Tests for emulated_roku config flow.""" """Tests for emulated_roku config flow."""
from homeassistant import config_entries
from homeassistant.components.emulated_roku import config_flow from homeassistant.components.emulated_roku import config_flow
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -6,10 +7,10 @@ from tests.common import MockConfigEntry
async def test_flow_works(hass): async def test_flow_works(hass):
"""Test that config flow works.""" """Test that config flow works."""
flow = config_flow.EmulatedRokuFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass config_flow.DOMAIN,
result = await flow.async_step_user( context={"source": config_entries.SOURCE_USER},
user_input={"name": "Emulated Roku Test", "listen_port": 8060} data={"name": "Emulated Roku Test", "listen_port": 8060},
) )
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
@ -22,10 +23,12 @@ async def test_flow_already_registered_entry(hass):
MockConfigEntry( MockConfigEntry(
domain="emulated_roku", data={"name": "Emulated Roku Test", "listen_port": 8062} domain="emulated_roku", data={"name": "Emulated Roku Test", "listen_port": 8062}
).add_to_hass(hass) ).add_to_hass(hass)
flow = config_flow.EmulatedRokuFlowHandler()
flow.hass = hass
result = await flow.async_step_user( result = await hass.config_entries.flow.async_init(
user_input={"name": "Emulated Roku Test", "listen_port": 8062} config_flow.DOMAIN,
context={"source": config_entries.SOURCE_USER},
data={"name": "Emulated Roku Test", "listen_port": 8062},
) )
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "already_configured"

View file

@ -5,7 +5,6 @@ from unittest.mock import ANY, AsyncMock, MagicMock, patch
from kostal.plenticore import PlenticoreAuthenticationException from kostal.plenticore import PlenticoreAuthenticationException
from homeassistant import config_entries, setup from homeassistant import config_entries, setup
from homeassistant.components.kostal_plenticore import config_flow
from homeassistant.components.kostal_plenticore.const import DOMAIN from homeassistant.components.kostal_plenticore.const import DOMAIN
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -188,16 +187,3 @@ async def test_already_configured(hass):
assert result2["type"] == "abort" assert result2["type"] == "abort"
assert result2["reason"] == "already_configured" assert result2["reason"] == "already_configured"
def test_configured_instances(hass):
"""Test configured_instances returns all configured hosts."""
MockConfigEntry(
domain="kostal_plenticore",
data={"host": "2.2.2.2", "password": "foobar"},
unique_id="112233445566",
).add_to_hass(hass)
result = config_flow.configured_instances(hass)
assert result == {"2.2.2.2"}

View file

@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, Mock, patch
import pytest import pytest
from homeassistant import data_entry_flow from homeassistant import config_entries, data_entry_flow
from homeassistant.components.logi_circle import config_flow from homeassistant.components.logi_circle import config_flow
from homeassistant.components.logi_circle.config_flow import ( from homeassistant.components.logi_circle.config_flow import (
DOMAIN, DOMAIN,
@ -13,7 +13,7 @@ from homeassistant.components.logi_circle.config_flow import (
) )
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import mock_coro from tests.common import MockConfigEntry, mock_coro
class MockRequest: class MockRequest:
@ -121,24 +121,26 @@ async def test_abort_if_no_implementation_registered(hass):
async def test_abort_if_already_setup(hass): async def test_abort_if_already_setup(hass):
"""Test we abort if Logi Circle is already setup.""" """Test we abort if Logi Circle is already setup."""
flow = init_config_flow(hass) flow = init_config_flow(hass)
MockConfigEntry(domain=config_flow.DOMAIN).add_to_hass(hass)
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): result = await hass.config_entries.flow.async_init(
result = await flow.async_step_user() config_flow.DOMAIN,
context={"source": config_entries.SOURCE_USER},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): result = await hass.config_entries.flow.async_init(
result = await flow.async_step_import() config_flow.DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): with pytest.raises(data_entry_flow.AbortFlow):
result = await flow.async_step_code() result = await flow.async_step_code()
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
with patch.object(hass.config_entries, "async_entries", return_value=[{}]): result = await flow.async_step_auth()
result = await flow.async_step_auth()
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "external_setup" assert result["reason"] == "external_setup"

View file

@ -189,7 +189,7 @@ async def test_duplicate_bridge_import(hass):
) )
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == CasetaConfigFlow.ABORT_REASON_ALREADY_CONFIGURED assert result["reason"] == "already_configured"
assert len(mock_setup_entry.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 0

View file

@ -1,7 +1,8 @@
"""Tests for the Twente Milieu config flow.""" """Tests for the Twente Milieu config flow."""
import aiohttp import aiohttp
from homeassistant import data_entry_flow from homeassistant import config_entries, data_entry_flow
from homeassistant.components.twentemilieu import config_flow
from homeassistant.components.twentemilieu.const import ( from homeassistant.components.twentemilieu.const import (
CONF_HOUSE_LETTER, CONF_HOUSE_LETTER,
CONF_HOUSE_NUMBER, CONF_HOUSE_NUMBER,
@ -83,7 +84,9 @@ async def test_address_already_set_up(
) )
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=FIXTURE_USER_INPUT config_flow.DOMAIN,
context={"source": config_entries.SOURCE_USER},
data=FIXTURE_USER_INPUT,
) )
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT

View file

@ -2755,3 +2755,60 @@ async def test_setup_retrying_during_shutdown(hass):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(mock_call.return_value.mock_calls) == 0 assert len(mock_call.return_value.mock_calls) == 0
@pytest.mark.parametrize(
"matchers, reason",
[
({}, "already_configured"),
({"host": "3.3.3.3"}, "no_match"),
({"host": "3.4.5.6"}, "already_configured"),
({"host": "3.4.5.6", "ip": "3.4.5.6"}, "no_match"),
({"host": "3.4.5.6", "ip": "1.2.3.4"}, "already_configured"),
({"host": "3.4.5.6", "ip": "1.2.3.4", "port": 23}, "already_configured"),
({"ip": "9.9.9.9"}, "already_configured"),
({"ip": "7.7.7.7"}, "no_match"), # ignored
],
)
async def test__async_abort_entries_match(hass, manager, matchers, reason):
"""Test aborting if matching config entries exist."""
MockConfigEntry(
domain="comp", data={"ip": "1.2.3.4", "host": "4.5.6.7", "port": 23}
).add_to_hass(hass)
MockConfigEntry(
domain="comp", data={"ip": "9.9.9.9", "host": "4.5.6.7", "port": 23}
).add_to_hass(hass)
MockConfigEntry(
domain="comp", data={"ip": "1.2.3.4", "host": "3.4.5.6", "port": 23}
).add_to_hass(hass)
MockConfigEntry(
domain="comp",
source=config_entries.SOURCE_IGNORE,
data={"ip": "7.7.7.7", "host": "4.5.6.7", "port": 23},
).add_to_hass(hass)
await async_setup_component(hass, "persistent_notification", {})
mock_setup_entry = AsyncMock(return_value=True)
mock_integration(hass, MockModule("comp", async_setup_entry=mock_setup_entry))
mock_entity_platform(hass, "config_flow.comp", None)
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Test user step."""
self._async_abort_entries_match(matchers)
return self.async_abort(reason="no_match")
with patch.dict(config_entries.HANDLERS, {"comp": TestFlow, "beer": 5}):
result = await manager.flow.async_init(
"comp", context={"source": config_entries.SOURCE_USER}
)
await hass.async_block_till_done()
assert result["type"] == "abort"
assert result["reason"] == reason