Skip to main content
This guide will walk you through creating a new integration for Home Assistant. We’ll cover the essential files, structure, and best practices for building a high-quality integration.

Prerequisites

Before you begin, ensure you have:
  • A working Home Assistant development environment
  • Python 3.12 or later installed
  • Familiarity with async/await patterns in Python
  • Understanding of the device or service you want to integrate

Integration Structure

A basic integration consists of several key files:
homeassistant/components/your_integration/
├── __init__.py           # Main integration setup
├── manifest.json         # Integration metadata
├── config_flow.py        # Configuration UI flow
├── const.py             # Constants and configuration keys
├── sensor.py            # Entity platform (example)
└── strings.json         # Translations for UI

Step 1: Create the Manifest

The manifest.json file defines your integration’s metadata. This is required for Home Assistant to recognize your integration.
manifest.json
{
  "domain": "your_integration",
  "name": "Your Integration",
  "codeowners": ["@your-github-username"],
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/integrations/your_integration",
  "iot_class": "cloud_polling",
  "requirements": ["your-library==1.0.0"],
  "version": "1.0.0"
}

Key Manifest Fields

  • domain: Unique identifier for your integration (lowercase, no spaces)
  • name: Human-readable name displayed in the UI
  • config_flow: Set to true if your integration uses the UI configuration flow
  • iot_class: How the integration communicates (see IoT class documentation)
  • requirements: Python packages required by your integration
  • codeowners: GitHub usernames responsible for maintaining this integration

Step 2: Define Constants

Create a const.py file to store configuration keys and constants:
const.py
"""Constants for the Your Integration integration."""

DOMAIN = "your_integration"

# Configuration keys
CONF_API_KEY = "api_key"
CONF_HOST = "host"

# Default values
DEFAULT_PORT = 8080
DEFAULT_SCAN_INTERVAL = 60

Step 3: Implement the Main Integration

The __init__.py file contains the core integration setup logic:
__init__.py
"""The Your Integration integration."""
from __future__ import annotations

import logging

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

# List of platforms to support
PLATFORMS: list[Platform] = [Platform.SENSOR]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Your Integration from a config entry."""
    # Store an API client or coordinator in hass.data
    hass.data.setdefault(DOMAIN, {})
    
    try:
        # Initialize your API client or data coordinator
        # Example: api_client = YourAPIClient(entry.data[CONF_API_KEY])
        # await api_client.async_connect()
        
        # Store the client for use by platforms
        hass.data[DOMAIN][entry.entry_id] = {
            "client": None,  # Your API client here
        }
    except Exception as err:
        raise ConfigEntryNotReady(f"Unable to connect: {err}") from err
    
    # Forward the setup to platforms
    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
    
    return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    # Unload platforms
    unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
    
    if unload_ok:
        # Clean up stored data
        hass.data[DOMAIN].pop(entry.entry_id)
    
    return unload_ok

Key Functions

async_setup_entry

This is the main entry point for modern integrations. It:
  1. Initializes API clients or data coordinators
  2. Stores shared data in hass.data[DOMAIN]
  3. Forwards setup to entity platforms
  4. Returns True on success or raises ConfigEntryNotReady on failure

async_unload_entry

Cleans up when the integration is removed:
  1. Unloads all entity platforms
  2. Cleans up stored data
  3. Closes API connections if needed

Step 4: Create a Config Flow

The config flow handles user configuration through the UI. See the Config Entries guide for detailed information.
config_flow.py
"""Config flow for Your Integration."""
from __future__ import annotations

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult

from .const import CONF_API_KEY, DOMAIN


class YourIntegrationConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
    """Handle a config flow for Your Integration."""
    
    VERSION = 1
    
    async def async_step_user(
        self, user_input: dict[str, Any] | None = None
    ) -> FlowResult:
        """Handle the initial step."""
        errors = {}
        
        if user_input is not None:
            # Validate the input
            try:
                # Test the connection with provided credentials
                # await validate_credentials(user_input[CONF_API_KEY])
                
                # Create the config entry
                return self.async_create_entry(
                    title="Your Integration",
                    data=user_input,
                )
            except Exception:
                errors["base"] = "cannot_connect"
        
        # Show the configuration form
        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema({
                vol.Required(CONF_API_KEY): str,
            }),
            errors=errors,
        )

Step 5: Create Entity Platforms

Entity platforms provide the actual functionality. For example, a sensor platform:
sensor.py
"""Sensor platform for Your Integration."""
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up sensor platform."""
    # Get the API client from hass.data
    client = hass.data[DOMAIN][entry.entry_id]["client"]
    
    # Create sensor entities
    async_add_entities([
        YourSensor(client, "temperature"),
        YourSensor(client, "humidity"),
    ])


class YourSensor(SensorEntity):
    """Representation of a Your Integration sensor."""
    
    def __init__(self, client, sensor_type):
        """Initialize the sensor."""
        self._client = client
        self._sensor_type = sensor_type
        self._attr_name = f"Your Integration {sensor_type}"
        self._attr_unique_id = f"{DOMAIN}_{sensor_type}"
    
    async def async_update(self) -> None:
        """Fetch new state data for the sensor."""
        # Update the sensor value
        # self._attr_native_value = await self._client.get_value(self._sensor_type)
        pass
See the Entity Component guide for more details on creating entities.

Step 6: Add Translations

Create a strings.json file for UI translations:
strings.json
{
  "config": {
    "step": {
      "user": {
        "title": "Connect to Your Integration",
        "description": "Enter your API credentials",
        "data": {
          "api_key": "API Key"
        }
      }
    },
    "error": {
      "cannot_connect": "Failed to connect, please check your API key"
    }
  }
}

Testing Your Integration

Before submitting your integration:
  1. Run tests: Ensure your integration has comprehensive test coverage
  2. Check code quality: Run pylint and mypy on your code
  3. Test manually: Install your integration in a development Home Assistant instance
  4. Review the checklist: Follow the integration quality scale requirements

Best Practices

Use DataUpdateCoordinator

For integrations that poll data, use DataUpdateCoordinator to efficiently manage updates:
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

coordinator = DataUpdateCoordinator(
    hass,
    _LOGGER,
    name="your_integration",
    update_method=async_update_data,
    update_interval=timedelta(seconds=60),
)

Handle Connection Errors

Always handle connection errors gracefully:
  • Raise ConfigEntryNotReady during setup if the device is temporarily unavailable
  • Raise ConfigEntryAuthFailed if authentication fails
  • Set entity available property based on connection status

Use Unique IDs

Always provide unique IDs for entities to enable customization:
self._attr_unique_id = f"{entry.entry_id}_{device_id}_{sensor_type}"

Follow the Quality Scale

Aim for at least Silver quality scale:
  • Use config flow (no YAML configuration)
  • Implement proper entity naming
  • Handle errors gracefully
  • Provide translations
  • Include tests

Next Steps

Common Patterns

Single Config Entry Integrations

For services that should only have one configuration:
manifest.json
{
  "single_config_entry": true
}

Virtual Integrations

For integrations that re-use another integration’s code:
manifest.json
{
  "integration_type": "virtual"
}

Resources

Build docs developers (and LLMs) love