Mark integrations as single_config_entry in manifest [a-i] (#128189)

* mark integrations as single_config_entry in manifest

* fix ecobee tests

* fix iaqualink test
This commit is contained in:
Michael 2024-10-12 08:59:57 +02:00 committed by GitHub
parent 1484a9c0ee
commit c50d0646ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 48 additions and 90 deletions

View File

@ -47,18 +47,12 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return await self.async_step_config()
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle a flow initialized by zeroconf discovery."""
if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
await self.async_set_unique_id(DOMAIN)
return await self.async_step_confirm()

View File

@ -15,5 +15,6 @@
"iot_class": "local_polling",
"loggers": ["casttube", "pychromecast"],
"requirements": ["PyChromecast==14.0.4"],
"single_config_entry": true,
"zeroconf": ["_googlecast._tcp.local."]
}

View File

@ -12,9 +12,6 @@
}
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
},
"error": {
"invalid_known_hosts": "Known hosts must be a comma separated list of hosts."
}

View File

@ -18,6 +18,4 @@ class CloudConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the system step."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return self.async_create_entry(title="Home Assistant Cloud", data={})

View File

@ -8,5 +8,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["hass_nabucasa"],
"requirements": ["hass-nabucasa==0.81.1"]
"requirements": ["hass-nabucasa==0.81.1"],
"single_config_entry": true
}

View File

@ -1,10 +1,4 @@
{
"config": {
"step": {},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
}
},
"system_health": {
"info": {
"can_reach_cert_server": "Reach certificate server",

View File

@ -118,9 +118,6 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
persistent_notification.async_dismiss(self.hass, "cloudflare_setup")
errors: dict[str, str] = {}

View File

@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/cloudflare",
"iot_class": "cloud_push",
"loggers": ["pycfdns"],
"requirements": ["pycfdns==3.0.0"]
"requirements": ["pycfdns==3.0.0"],
"single_config_entry": true
}

View File

@ -34,8 +34,7 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"services": {

View File

@ -39,9 +39,6 @@ class DemoConfigFlow(ConfigFlow, domain=DOMAIN):
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Set the config entry up from yaml."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return self.async_create_entry(title="Demo", data=import_data)

View File

@ -5,5 +5,6 @@
"dependencies": ["conversation", "group", "zone"],
"documentation": "https://www.home-assistant.io/integrations/demo",
"iot_class": "calculated",
"quality_scale": "internal"
"quality_scale": "internal",
"single_config_entry": true
}

View File

@ -34,9 +34,6 @@ class DuoTecnoConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
errors: dict[str, str] = {}
if user_input is not None:
try:

View File

@ -7,5 +7,6 @@
"iot_class": "local_push",
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
"quality_scale": "silver",
"requirements": ["pyDuotecno==2024.10.0"]
"requirements": ["pyDuotecno==2024.10.0"],
"single_config_entry": true
}

View File

@ -13,9 +13,6 @@
}
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",

View File

@ -29,10 +29,6 @@ class EcobeeFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
if self._async_current_entries():
# Config entry already exists, only one allowed.
return self.async_abort(reason="single_instance_allowed")
errors = {}
stored_api_key = (
self.hass.data[DATA_ECOBEE_CONFIG].get(CONF_API_KEY)

View File

@ -10,6 +10,7 @@
"iot_class": "cloud_polling",
"loggers": ["pyecobee"],
"requirements": ["python-ecobee-api==0.2.20"],
"single_config_entry": true,
"zeroconf": [
{
"type": "_ecobee._tcp.local."

View File

@ -38,9 +38,6 @@ class EnOceanFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle an EnOcean config flow start."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return await self.async_step_detect()
async def async_step_detect(

View File

@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/enocean",
"iot_class": "local_push",
"loggers": ["enocean"],
"requirements": ["enocean==0.50"]
"requirements": ["enocean==0.50"],
"single_config_entry": true
}

View File

@ -18,8 +18,7 @@
"invalid_dongle_path": "No valid dongle found for this path"
},
"abort": {
"invalid_dongle_path": "Invalid dongle path",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
"invalid_dongle_path": "Invalid dongle path"
}
}
}

View File

@ -27,11 +27,6 @@ class AqualinkFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow start."""
# Supporting a single account.
entries = self._async_current_entries()
if entries:
return self.async_abort(reason="single_instance_allowed")
errors = {}
if user_input is not None:

View File

@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/iaqualink",
"iot_class": "cloud_polling",
"loggers": ["iaqualink"],
"requirements": ["iaqualink==0.5.0", "h2==4.1.0"]
"requirements": ["iaqualink==0.5.0", "h2==4.1.0"],
"single_config_entry": true
}

View File

@ -13,9 +13,6 @@
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
}
}
}

View File

@ -30,9 +30,6 @@ class IBeaconConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if not bluetooth.async_scanner_count(self.hass, connectable=False):
return self.async_abort(reason="bluetooth_not_available")

View File

@ -13,5 +13,6 @@
"documentation": "https://www.home-assistant.io/integrations/ibeacon",
"iot_class": "local_push",
"loggers": ["bleak"],
"requirements": ["ibeacon-ble==1.2.0"]
"requirements": ["ibeacon-ble==1.2.0"],
"single_config_entry": true
}

View File

@ -6,8 +6,7 @@
}
},
"abort": {
"bluetooth_not_available": "At least one Bluetooth adapter or remote must be configured to use iBeacon Tracker.",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
"bluetooth_not_available": "At least one Bluetooth adapter or remote must be configured to use iBeacon Tracker."
}
},
"options": {

View File

@ -59,8 +59,6 @@ class InsteonFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Init the config flow."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
modem_types = [STEP_PLM, STEP_HUB_V1, STEP_HUB_V2]
return self.async_show_menu(step_id="user", menu_options=modem_types)
@ -135,9 +133,6 @@ class InsteonFlowHandler(ConfigFlow, domain=DOMAIN):
self, discovery_info: usb.UsbServiceInfo
) -> ConfigFlowResult:
"""Handle USB discovery."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
self._device_path = discovery_info.device
self._device_name = usb.human_readable_device_name(
discovery_info.device,

View File

@ -20,6 +20,7 @@
"pyinsteon==1.6.3",
"insteon-frontend-home-assistant==0.5.0"
],
"single_config_entry": true,
"usb": [
{
"vid": "10BF"

View File

@ -44,7 +44,6 @@
},
"abort": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"not_insteon_device": "Discovered device not an Insteon device"
}
},

View File

@ -29,10 +29,6 @@ class ISSConfigFlow(ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
# Check if already configured
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if user_input is not None:
return self.async_create_entry(
title=DEFAULT_NAME,

View File

@ -7,5 +7,6 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pyiss"],
"requirements": ["pyiss==1.0.1"]
"requirements": ["pyiss==1.0.1"],
"single_config_entry": true
}

View File

@ -6,7 +6,6 @@
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"latitude_longitude_not_defined": "Latitude and longitude are not defined in Home Assistant."
}
},

View File

@ -958,7 +958,8 @@
"name": "Cloudflare",
"integration_type": "hub",
"config_flow": true,
"iot_class": "cloud_push"
"iot_class": "cloud_push",
"single_config_entry": true
},
"cmus": {
"name": "cmus",
@ -1160,7 +1161,8 @@
"demo": {
"integration_type": "hub",
"config_flow": false,
"iot_class": "calculated"
"iot_class": "calculated",
"single_config_entry": true
},
"denon": {
"name": "Denon",
@ -1403,7 +1405,8 @@
"name": "Duotecno",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_push"
"iot_class": "local_push",
"single_config_entry": true
},
"duquesne_light": {
"name": "Duquesne Light",
@ -1461,7 +1464,8 @@
"name": "ecobee",
"integration_type": "hub",
"config_flow": true,
"iot_class": "cloud_polling"
"iot_class": "cloud_polling",
"single_config_entry": true
},
"ecoforest": {
"name": "Ecoforest",
@ -1659,7 +1663,8 @@
"name": "EnOcean",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_push"
"iot_class": "local_push",
"single_config_entry": true
},
"enphase_envoy": {
"name": "Enphase Envoy",
@ -2732,7 +2737,8 @@
"name": "Jandy iAqualink",
"integration_type": "hub",
"config_flow": true,
"iot_class": "cloud_polling"
"iot_class": "cloud_polling",
"single_config_entry": true
},
"ibm": {
"name": "IBM",
@ -2861,7 +2867,8 @@
"name": "Insteon",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_push"
"iot_class": "local_push",
"single_config_entry": true
},
"intellifire": {
"name": "IntelliFire",
@ -2960,7 +2967,8 @@
"name": "International Space Station (ISS)",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
"iot_class": "cloud_polling",
"single_config_entry": true
},
"ista_ecotrend": {
"name": "ista EcoTrend",

View File

@ -11,6 +11,7 @@ from homeassistant.components.ecobee.const import (
DATA_ECOBEE_CONFIG,
DOMAIN,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@ -20,12 +21,11 @@ from tests.common import MockConfigEntry
async def test_abort_if_already_setup(hass: HomeAssistant) -> None:
"""Test we abort if ecobee is already setup."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
MockConfigEntry(domain=DOMAIN).add_to_hass(hass)
result = await flow.async_step_user()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "single_instance_allowed"

View File

@ -7,7 +7,8 @@ from iaqualink.exception import (
AqualinkServiceUnauthorizedException,
)
from homeassistant.components.iaqualink import config_flow
from homeassistant.components.iaqualink import DOMAIN, config_flow
from homeassistant.config_entries import SOURCE_USER
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@ -18,13 +19,12 @@ async def test_already_configured(
"""Test config flow when iaqualink component is already setup."""
config_entry.add_to_hass(hass)
flow = config_flow.AqualinkFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_user(config_data)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "single_instance_allowed"
async def test_without_config(hass: HomeAssistant) -> None: