Test the connection in the config flow
Bronze rule test-before-configure: the config flow tests the connection before it creates the entry.
This is a Bronze rule. In quality_scale.yaml, it is called test-before-configure.
Before the config flow creates the config entry, it must test that the integration can connect with what the user entered. When the test fails, the flow shows the form again with an error that tells the user what went wrong.
A config flow that accepts anything leaves the user with an integration that does not work, and no clue why. Many problems are easy to catch at this point: a typo in the IP address, a wrong port, a firewall in the way, a DNS problem, wrong credentials, or a device that is not supported. Catching them here, while the user is still looking at the form, is far friendlier than an error in the logs later.
Make a real call with the library, like fetching the device information, and map the exceptions of the library to errors on the form:
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow started by the user."""
errors: dict[str, str] = {}
if user_input is not None:
client = MyClient(
user_input[CONF_HOST], async_get_clientsession(self.hass)
)
try:
await client.get_device_info()
except MyConnectionError:
errors["base"] = "cannot_connect"
except MyAuthenticationError:
errors["base"] = "invalid_auth"
except Exception:
LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
return self.async_create_entry(
title="My integration", data=user_input
)
return self.async_show_form(
step_id="user",
data_schema=probatio.Schema({probatio.Required(CONF_HOST): str}),
errors=errors,
)The error keys go in strings.json, under config.error. Common errors like cannot_connect, invalid_auth, and unknown have shared translations you can reference with [%key:common::config_flow::error::cannot_connect%].
- Pylint, when the rule is
done:home-assistant-missing-test-before-configurewarns when a config flow creates entries but shows no sign of reporting a failure: noerrors=passed to a form, and no abort inside anexcept. OAuth2 flows are skipped, because the token exchange is the connection test. - Whether the test makes sense for the device is checked in review.
- Integrations that do not connect to a device or service, like helpers, have nothing to test and are exempt.
- Integrations that discover their devices at runtime, without connection details from the user (like Google Cast), are exempt.
- config-flow: set up from the UI with a config flow.
- unique-config-entry: prevent duplicate config entries.
- config-flow-test-coverage: test the config flow fully.
- discovery: support discovery.
- reauthentication-flow: reauthenticate from the UI.
- reconfiguration-flow: reconfigure from the UI.