Repository: home-assistant/core
File: homeassistant/components/nrgkick/config_flow.py (Python)

Code before:

        # Extract device information from mDNS metadata.
        serial = discovery_info.properties.get("serial_number")
        device_name = discovery_info.properties.get("device_name")
        model_type = discovery_info.properties.get("model_type")
        json_api_enabled = discovery_info.properties.get("json_api_enabled", "0")

        if not serial:
            _LOGGER.debug("NRGkick device discovered without serial number")
            return self.async_abort(reason="no_serial_number")

        # Set unique ID to prevent duplicate entries.
        await self.async_set_unique_id(serial)
        # Update the host if the device is already configured (IP might have changed).
        self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host})

        # Store discovery info for the confirmation step.
        self._discovered_host = discovery_info.host
        # Fallback: device_name -> model_type -> "NRGkick".
        discovered_name = device_name or model_type or "NRGkick"
        self._discovered_name = discovered_name
        self.context["title_placeholders"] = {"name": discovered_name}

        # If JSON API is disabled, guide the user through enabling it.
        if json_api_enabled != "1":
            _LOGGER.debug("NRGkick device %s does not have JSON API enabled", serial)
            return await self.async_step_zeroconf_enable_json_api()

        try:
            await validate_input(self.hass, self._discovered_host)
        except NRGkickApiClientAuthenticationError:
            self._pending_host = self._discovered_host
            return await self.async_step_user_auth()
        except NRGkickApiClientApiDisabledError:
            # mDNS metadata may be stale; fall back to the enable guidance.
            return await self.async_step_zeroconf_enable_json_api()
        except (
            NRGkickApiClientCommunicationError,
            NRGkickApiClientInvalidResponseError,
        ):
            return self.async_abort(reason="cannot_connect")
        except NRGkickApiClientError:
            _LOGGER.exception("Unexpected error")
            return self.async_abort(reason="unknown")

        # Proceed to confirmation step (no auth required upfront).
        return await self.async_step_zeroconf_confirm()

    async def async_step_zeroconf_enable_json_api(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:

Review comment (bug):
If I read your code correct, the `_async_validate_credentials` can crash and raises exceptions Where in this `async_step_reconfigure` are we catching them? :)

Code after:

        return self.async_show_form(
            step_id="reauth_confirm",
            data_schema=self.add_suggested_values_to_schema(
                STEP_AUTH_DATA_SCHEMA,
                self._get_reauth_entry().data,
            ),
            errors=errors,
        )

    async def async_step_reconfigure(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle reconfiguration of the integration."""
        errors: dict[str, str] = {}
        reconfigure_entry = self._get_reconfigure_entry()
        if user_input is not None:
            try:
                host = _normalize_host(user_input[CONF_HOST])
            except vol.Invalid:
                errors["base"] = "cannot_connect"
            else:
                info, needs_auth = await self._async_validate_host(host, errors)
                if needs_auth:
                    self._pending_host = host
                    return await self.async_step_reconfigure_auth()
                if info:
                    await self.async_set_unique_id(
                        info["serial"], raise_on_progress=False
                    )
                    self._abort_if_unique_id_mismatch()
                    return self.async_update_reload_and_abort(
                        reconfigure_entry,
                        data_updates={CONF_HOST: host},
                    )

        return self.async_show_form(
            step_id="reconfigure",
            data_schema=self.add_suggested_values_to_schema(
                STEP_USER_DATA_SCHEMA,
                reconfigure_entry.data,
            ),
            errors=errors,
        )

    async def async_step_reconfigure_auth(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle reconfiguration authentication step."""
        errors: dict[str, str] = {}