smithery.ai

homeassistant-config-flow

Patterns for Home Assistant config flows, discovery handlers, options flows, and reauth for custom components

First seen Apr 27, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,516 B
  • docs SUMMARY.md 142 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Home Assistant Config Flow & Discovery

Use this skill when adding or adjusting setup flows, discovery handlers, options flows, or reauth for this integration.

When to Use

  • Creating or refining config_flow.py
  • Handling discovery sources (user, dhcp, zeroconf, integration discovery)
  • Adding/updating options flow or reauth steps
  • Ensuring duplicate prevention and IP-change handling are correct

Core Principles

  • UI-first: No new YAML; all setup in the UI.
  • Async-only: All I/O must be awaited; use executor only for blocking library work.
  • One device, one entry: Abort duplicates; update existing entries when discovery reports a new host/IP.
  • Stable identifiers: Use BLE-MAC (or WiFi MAC) for unique IDs; never pivot unique IDs on IP.
  • Selectors > raw text: Prefer HA selectors for better UX and validation.

Step Patterns

  • asyncstepuser: show form when userinput is None; on submit, validate connectivity; return errors with keys (cannotconnect, invalidauth, alreadyconfigured).
  • asyncstepdhcp / asyncstepzeroconf / asyncstepintegrationdiscovery: deduplicate via MAC/unique ID; if existing entry with new host, update data and abort with alreadyconfigured.
  • asyncstepconfirm: for discovery flows, prefill known values and ask user to confirm.

Reauth Flow Pattern

Reauth handles authentication/connection failures gracefully, allowing users to update credentials or IP addresses without removing the integration.

Implementation Steps

  1. Entry point: asyncstepreauth(entry_data) - Called when reauth is triggered
  2. Confirm step: asyncstepreauthconfirm(userinput) - Show form, validate, update entry

Code Pattern

from homeassistant.config_entries import SOURCE_REAUTH

async def async_step_reauth(
    self, entry_data: dict[str, Any]
) -> ConfigFlowResult:
    """Handle reauth when device becomes unreachable."""
    return await self.async_step_reauth_confirm()

async def async_step_reauth_confirm(
    self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
    """Confirm reauth dialog."""
    errors: dict[str, str] = {}
    reauth_entry = self._get_reauth_entry()

    if user_input is not None:
        host = user_input.get(CONF_HOST)
        try:
            device_info = await get_device_info(host=host, port=port)
            if device_info:
                # Validate unique ID unchanged
                await self.async_set_unique_id(device_info["ble_mac"])
                self._abort_if_unique_id_mismatch()
                # Preferred: use async_update_reload_and_abort helper
                return self.async_update_reload_and_abort(
                    reauth_entry,
                    data_updates={CONF_HOST: host},
                )
            errors["base"] = "cannot_connect"
        except (OSError, TimeoutError):
            errors["base"] = "cannot_connect"

    return self.async_show_form(
        step_id="reauth_confirm",
        data_schema=vol.Schema({vol.Required(CONF_HOST): cv.string}),
        errors=errors,
        description_placeholders={"host": reauth_entry.data.get(CONF_HOST, "")},
    )

Key Helpers

  • self.getreauth_entry() - Get the config entry being reauthenticated
  • self.asyncupdatereloadandabort() - Update entry, reload, and abort with reauth_successful (preferred)
  • self.abortifuniqueid_mismatch() - Ensure the same device is being reauthenticated
  • Check source: if self.source == SOURCE_REAUTH:

Triggering Reauth

From coordinator or setup when connection fails repeatedly:

entry.async_start_reauth(hass)

Required Translations

Add to strings.json and translations/en.json:

"reauth_confirm": {
  "title": "Reconnect Device",
  "description": "The device at {host} is not responding.",
  "data": {"host": "IP address"}
}

And abort reason:

"abort": {
  "reauth_successful": "Device reconnected successfully"
}

Form Patterns

Grouping Input Fields (sections)

Use section() to group related fields into collapsible sections:

from homeassistant.data_entry_flow import section

data_schema = {
    vol.Required("host"): str,
    vol.Required("advanced_options"): section(
        vol.Schema({
            vol.Optional("timeout", default=30): int,
            vol.Optional("retry_count", default=3): int,
        }),
        {"collapsed": True},  # Initially collapsed
    )
}

Pre-filling Forms with Suggested Values

from homeassistant.helpers.schema_config_entry_flow import add_suggested_values_to_schema

return self.async_show_form(
    data_schema=add_suggested_values_to_schema(
        OPTIONS_SCHEMA, self.config_entry.options
    )
)

Browser Autofill

Use recognized keys (username, password) or TextSelector with autocomplete:

vol.Required(CONF_USERNAME): TextSelector(
    TextSelectorConfig(type=TextSelectorType.EMAIL, autocomplete="username")
),

Read-only Fields in Options

For frozen configuration shown in options:

vol.Optional(CONF_DEVICE_ID): EntitySelector(
    EntitySelectorConfig(read_only=True)
),

Navigation Menu

return self.async_show_menu(
    step_id="user",
    menu_options=["discovery", "manual"],
    description_placeholders={"model": "Venus A"},
)

Long-running Tasks (Show Progress)

if not self.task:
    self.task = self.hass.async_create_task(long_running_operation())
if not self.task.done():
    return self.async_show_progress(
        progress_action="connecting",
        progress_task=self.task,
    )
return self.async_show_progress_done(next_step_id="finish")

Options Flow

  • Implement asyncgetoptions_flow to return an OptionsFlowHandler.
  • Store runtime preferences in entry.options; keep credentials in entry.data.
  • Register entry.addupdatelistener in init.py to reload on options change.
  • Use selectors for numeric intervals, toggles, enums, and text fields where helpful.
  • Use addsuggestedvaluestoschema() to pre-fill current options.

Discovery Handling

  • Manifest-driven discovery (dhcp/zeroconf/ssdp) should land in dedicated steps.
  • The scanner handles IP changes; do not add fallback discovery inside coordinator updates.
  • On discovery, set unique ID early and call self.abortifuniqueid_configured().
  • If the device is known but host changed, update the entry data and abort with already_configured.

Error Keys & Translations

  • Define user-facing errors in strings.json and mirror to translations/en.json.
  • Preferred keys: cannotconnect, invalidauth, already_configured, unknown.
  • Provide helpful descriptionplaceholders when useful (e.g., {ipaddress}).

Form Translation Structure

Config flow forms are translated via strings.json:

{
  "config": {
    "step": {
      "user": {
        "title": "Set up Marstek",
        "description": "Connect to your {model} device.",
        "data": {
          "host": "Hostname or IP",
          "port": "Port"
        },
        "data_description": {
          "host": "The IP address of your Marstek device on the local network"
        },
        "sections": {
          "advanced_options": {
            "name": "Advanced Options",
            "description": "Optional configuration"
          }
        }
      }
    },
    "error": {
      "cannot_connect": "Cannot connect to device",
      "invalid_auth": "Invalid authentication"
    },
    "abort": {
      "already_configured": "Device is already configured",
      "reauth_successful": "Reauthentication successful"
    }
  }
}

Form translation keys:

  • title - Form title
  • description - Form description (supports placeholders via description_placeholders)
  • data - Field labels (keyed by field name)
  • data_description - Field help text (shown below field)
  • sections - Section names and descriptions (for section() fields)

Validation Checklist

  • Unique ID set from BLE-MAC/WiFi MAC; duplicates abort
  • User step validates connectivity asynchronously
  • Discovery steps update existing entry on host/IP change
  • Options flow reloads entry on change
  • Reauth asks only for the changed credential
  • Selectors used where appropriate; translations updated