
ha-integration
Develop custom Home Assistant integrations, config flows, entities, and platforms. Use when working with manifest.json, custom components, configflow.py, entity base classes, or device registry. Activates on keywords: integration, custom component, config flow, entity, platform, manifest.json, deviceinfo.
- Installs
- 72
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill ha-integration- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads ha-integration automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Home Assistant Integration Development
Create professional-grade custom Home Assistant integrations with complete config flows and entity implementations.
⚠️ BEFORE YOU START
This skill prevents 8 common integration errors and saves ~40% implementation time.
| Metric | Without Skill | With Skill |
|---|---|---|
| Setup Time | 45 minutes | 12 minutes |
| Common Errors | 8 | 0 |
| Config Flow Issues | 5+ | 0 |
| Entity Registration Bugs | 4+ | 0 |
Known Issues This Skill Prevents
- Missing manifest.json dependencies - Forgetting to declare required Home Assistant components
- Async/await issues - Not properly awaiting coordinator updates and entity initialization
- Entity state class mismatches - Using wrong STATE_CLASS (measurement vs total) for sensor platforms
- Config flow schema errors - Invalid vol.Schema definitions causing validation failures
- Device info not linked - Entities created without proper device registry connections
- Coordinator errors - Not handling data update failures gracefully
- Platform import timing - Loading platform files before component initialization
- Missing unique ID generation - Creating duplicate entities across restarts
Quick Start
Step 1: Create manifest.json
{
"domain": "my_integration",
"name": "My Integration",
"codeowners": ["@username"],
"config_flow": true,
"documentation": "https://github.com/username/ha-my-integration",
"requirements": [],
"version": "0.0.1"
}
Why this matters: The manifest.json defines integration metadata, declares dependencies, and enables config flow UI in Home Assistant.
Step 2: Create init.py with async setup
import asyncio
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from .coordinator import MyDataUpdateCoordinator
DOMAIN = "my_integration"
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the integration from config entry."""
hass.data.setdefault(DOMAIN, {})
# Create coordinator
coordinator = MyDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id] = coordinator
# Forward setup to platforms
await hass.config_entries.async_forward_entry_setups(entry, ["sensor"])
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload the integration."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, ["sensor"])
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
Why this matters: Proper async initialization ensures Home Assistant waits for data loading and platform setup completes before continuing.
Step 3: Create config_flow.py with validation
from typing import Any, Dict, Optional
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigEntry
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from .const import DOMAIN
class MyIntegrationConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle config flow for my_integration."""
async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None) -> FlowResult:
"""Handle user initiation of config flow."""
errors = {}
if user_input is not None:
# Validate user input
try:
# Validate connection or API call
pass
except Exception as exc:
errors["base"] = "invalid_auth"
if not errors:
# Create unique entry
await self.async_set_unique_id(user_input.get("host"))
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input.get("name"),
data=user_input
)
# Show form
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required("name"): str,
vol.Required("host"): str,
}),
errors=errors
)
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry):
"""Return options flow for this integration."""
return MyIntegrationOptionsFlow(config_entry)
Why this matters: Config flows provide user-friendly setup UI and validate input before creating config entries.
Critical Rules
✅ Always Do
- ✅ Use async/await throughout (async_setup_entry, async_added_to_hass, async_update_data)
- ✅ Generate unique_id for each entity (prevents duplicates on restart)
- ✅ Link entities to devices via device_info property
- ✅ Handle coordinator update failures gracefully (log, mark unavailable)
- ✅ Declare all external dependencies in manifest.json requirements
- ✅ Use type hints for better IDE support and Home Assistant compliance
- ✅ Register entities via coordinator patterns (DataUpdateCoordinator)
❌ Never Do
- ❌ Use synchronous network calls (requests library) - use aiohttp
- ❌ Import platform files at component level - let Home Assistant forward setup
- ❌ Create entities without unique_id - causes duplicates on restart
- ❌ Ignore coordinator update failures - mark entities unavailable
- ❌ Hardcode API endpoints - use config flow to store them
- ❌ Forget device_info when implementing multi-device integrations
- ❌ Use STATE_CLASS incorrectly (measurement vs total vs total_increasing)
Common Mistakes
❌ Wrong:
# Synchronous network call - blocks event loop
import requests
data = requests.get("https://api.example.com/data").json()
# No unique_id - duplicate entities on restart
class MySensor(SensorEntity):
pass
# Missing await
coordinator.async_refresh()
✅ Correct:
# Async network call - doesn't block
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/data") as resp:
data = await resp.json()
# Proper unique_id generation
class MySensor(SensorEntity):
@property
def unique_id(self) -> str:
return f"{self.coordinator.data['id']}_sensor"
# Proper await
await coordinator.async_request_refresh()
Why: Synchronous calls block Home Assistant's event loop, causing UI freezes. Missing unique_id causes entity duplicates. Missing await means code continues before async operation completes.
Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
| Duplicate entities on restart | No unique_id set | Implement unique_id property with stable identifier |
| Config flow validation fails silently | Missing error handling in async_step_user | Wrap validation in try/except, set errors dict |
| Entity state doesn't update | Coordinator not refreshing or entity not subscribed | Use @callback decorator for update listeners |
| Device not appearing | Missing device_info or device_identifier mismatch | Set device_info with identifiers matching registry |
| UI freezes during setup | Synchronous network calls in async_setup_entry | Use aiohttp for all async network operations |
| Platform imports fail | Importing platform files in init.py | Let Home Assistant handle via async_forward_entry_setups |
Manifest Configuration Reference
manifest.json
{
"domain": "integration_name",
"name": "Integration Display Name",
"codeowners": ["@github_username"],
"config_flow": true,
"documentation": "https://github.com/username/repo",
"homeassistant": "2024.1.0",
"requirements": ["requests>=2.25.0"],
"version": "1.0.0",
"issue_tracker": "https://github.com/username/repo/issues"
}
Key settings:
domain: Unique identifier (alphanumeric, underscores, lowercase)config_flow: Set to true to enable config UIrequirements: List of PyPI packages needed (e.g., ["requests>=2.25.0"])homeassistant: Minimum Home Assistant version required
Config Flow Patterns
Schema with vol.All for validation
vol.Schema({
vol.Required("host"): vol.All(str, vol.Length(min=5)),
vol.Required("port", default=8080): int,
vol.Optional("api_key"): str,
})
Reauth flow for expired credentials
async def async_step_reauth(self, user_input: Dict[str, Any] | None = None) -> FlowResult:
"""Handle reauth upon an API authentication error."""
config_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
if user_input is not None:
config_entry.data = {**config_entry.data, **user_input}
self.hass.config_entries.async_update_entry(config_entry)
return self.async_abort(reason="reauth_successful")
return self.async_show_form(
step_id="reauth",
data_schema=vol.Schema({vol.Required("api_key"): str})
)
Entity Implementation Patterns
Sensor with State Class
from homeassistant.components.sensor import SensorEntity, SensorStateClass
from homeassistant.const import UnitOfTemperature
class TemperatureSensor(SensorEntity):
"""Temperature sensor entity."""
_attr_device_class = "temperature"
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
def __init__(self, coordinator, idx):
"""Initialize sensor."""
self.coordinator = coordinator
self._idx = idx
@property
def unique_id(self) -> str:
"""Return unique ID."""
return f"{self.coordinator.data['id']}_temp_{self._idx}"
@property
def device_info(self) -> DeviceInfo:
"""Return device information."""
return DeviceInfo(
identifiers={(DOMAIN, self.coordinator.data['id'])},
name=self.coordinator.data['name'],
manufacturer="My Company",
)
@property
def native_value(self) -> float | None:
"""Return sensor value."""
try:
return float(self.coordinator.data['temperature'])
except (KeyError, TypeError):
return None
async def async_added_to_hass(self) -> None:
"""Connect to coordinator when added."""
await super().async_added_to_hass()
self.async_on_remove(
self.coordinator.async_add_listener(self._handle_coordinator_update)
)
@callback
def _handle_coordinator_update(self) -> None:
"""Update when coordinator updates."""
self.async_write_ha_state()
Binary Sensor
from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass
class MotionSensor(BinarySensorEntity):
"""Motion detection sensor."""
_attr_device_class = BinarySensorDeviceClass.MOTION
@property
def is_on(self) -> bool | None:
"""Return True if motion detected."""
return self.coordinator.data.get('motion', False)
DataUpdateCoordinator Pattern
from datetime import timedelta
from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator,
UpdateFailed,
)
import logging
_LOGGER = logging.getLogger(__name__)
class MyDataUpdateCoordinator(DataUpdateCoordinator):
"""Coordinator for fetching data."""
def __init__(self, hass, entry):
"""Initialize coordinator."""
super().__init__(
hass,
_LOGGER,
name="My Integration",
update_interval=timedelta(minutes=5),
)
self.entry = entry
async def _async_update_data(self):
"""Fetch data from API."""
try:
async with aiohttp.ClientSession() as session:
async with session.get(
f"https://api.example.com/data",
headers={"Authorization": f"Bearer {self.entry.data['api_key']}"}
) as resp:
if resp.status == 401:
raise ConfigEntryAuthFailed("Invalid API key")
return await resp.json()
except asyncio.TimeoutError as err:
raise UpdateFailed("API timeout") from err
except Exception as err:
raise UpdateFailed(f"API error: {err}") from err
Device Registry Patterns
Creating device with identifiers
from homeassistant.helpers.device_registry import DeviceInfo
device_info = DeviceInfo(
identifiers={(DOMAIN, "device_unique_id")},
name="Device Name",
manufacturer="Manufacturer",
model="Model Name",
sw_version="1.0.0",
via_device=(DOMAIN, "parent_device_id"), # For child devices
)
Serial number and connections
device_info = DeviceInfo(
identifiers={(DOMAIN, device_id)},
serial_number="SERIAL123",
connections={(dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:dd:ee:ff")},
)
Common Patterns
Loading config from config entry
class MyIntegration:
def __init__(self, hass: HomeAssistant, entry: ConfigEntry):
self.hass = hass
self.entry = entry
self.api_key = entry.data.get("api_key")
self.host = entry.data.get("host")
Handling options flow
async def async_step_init(self, user_input: Optional[Dict[str, Any]] = None) -> FlowResult:
"""Manage integration options."""
if user_input is not None:
return self.async_create_entry(
title="",
data=user_input
)
current_options = self.config_entry.options
return self.async_show_form(
step_id="init",
data_schema=vol.Schema({
vol.Optional("refresh_rate", default=current_options.get("refresh_rate", 5)): int,
})
)
Bundled Resources
References
Located in references/:
manifest-reference.md- Complete manifest.json field referenceentity-base-classes.md- Entity implementation base classes and propertiesconfig-flow-patterns.md- Advanced config flow patterns and validation
Templates
Located in assets/:
manifest.json- Starter manifest.json templateconfig_flow.py- Basic config flow boilerplate__init__.py- Component initialization templatecoordinator.py- DataUpdateCoordinator template
Note: For deep dives on specific topics, see the reference files above.
Dependencies
Required
| Package | Version | Purpose |
|---|---|---|
| homeassistant | >=2024.1.0 | Home Assistant core |
| voluptuous | >=0.13.0 | Config validation schemas |
Optional
| Package | Version | Purpose |
|---|---|---|
| aiohttp | >=3.8.0 | Async HTTP requests (for API integrations) |
| pyyaml | >=5.4 | YAML parsing (for config file integrations) |
Official Documentation
- Creating a Component - Home Assistant Developers
- Config Entries - Home Assistant Developers
- Entity Index - Home Assistant Developers
- Device Registry - Home Assistant Developers
Troubleshooting
Entity appears multiple times after restart
Symptoms: Same sensor/switch/light appears 2+ times in Home Assistant after reboot
Solution:
# Add unique_id property to entity class
@property
def unique_id(self) -> str:
return f"{self.coordinator.data['id']}_{self.platform}_{self._attr_name}"
Config flow validation never completes
Symptoms: Form hangs when submitting, no error displayed
Solution:
# Ensure all async operations are awaited and errors caught
async def async_step_user(self, user_input=None):
errors = {}
if user_input is not None:
try:
await self._validate_input(user_input) # ← Add await
except Exception as e:
errors["base"] = "validation_error" # ← Set error
if not errors:
return self.async_create_entry(...)
Entities show unavailable after update
Symptoms: All entities turn unavailable after coordinator update
Solution:
# Handle coordinator errors gracefully
async def _async_update_data(self):
try:
return await self.api.fetch_data()
except Exception as err:
raise UpdateFailed(f"Error: {err}") from err # ← Raises UpdateFailed, not Exception
Device doesn't appear in device registry
Symptoms: Device created but not visible in Home Assistant devices
Solution:
# Ensure device_info is returned by ALL entities for the device
@property
def device_info(self) -> DeviceInfo:
return DeviceInfo(
identifiers={(DOMAIN, self.coordinator.data['id'])}, # ← Must be consistent
name=self.coordinator.data['name'],
manufacturer="Manufacturer",
)
Setup Checklist
Before implementing a new integration, verify:
- Domain name is unique and follows lowercase-with-underscores convention
- manifest.json created with domain, name, and codeowners
- Config flow or manual configuration method implemented
- All async functions properly awaited
- Unique IDs generated for all entities (prevents duplicates)
- Device info linked if multi-device integration
- DataUpdateCoordinator or equivalent polling pattern
- Error handling with UpdateFailed exceptions
- Type hints on all function signatures
- Tests written for config flow validation
- Documentation URL in manifest points to valid location
FAQ
What does the ha-integration skill do?
Develop custom Home Assistant integrations, config flows, entities, and platforms. Use when working with manifest.json, custom components, configflow.py, entity base classes, or device registry. Activates on keywords: integration, custom component, config flow, entity, platform, manifest.json, deviceinfo.
How do I install ha-integration?
Run npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill ha-integration in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/ha-integration/ (personal) or .claude/skills/ha-integration/ (project).
Which AI agents can use ha-integration?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is ha-integration safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains ha-integration?
It is published by nodnarbnitram in nodnarbnitram/claude-code-extensions. It has 72 recorded installs.
More from nodnarbnitram/claude-code-extensions
tauri-v2
nodnarbnitram/claude-code-extensions
Tauri v2+ cross-platform app development with Rust backend. Use when configuring tauri.conf.json, implementing Rust commands ([tauri::command]), setting up IPC patterns (invoke, emit, channels), configuring permissions/capabilities, troubleshooting build issues, or deploying desktop/mobile apps. Triggers on Tauri, src-tauri, invoke, emit, capabilities.json.
code-reviewer
nodnarbnitram/claude-code-extensions
Review code for best practices, security issues, and potential bugs. Use when reviewing code changes, checking PRs, analyzing code quality, or performing security audits.
ha-voice
nodnarbnitram/claude-code-extensions
Configure Home Assistant Assist voice control with pipelines, intents, wake words, and speech processing. Use when setting up voice control, creating custom intents, configuring TTS/STT, or building voice satellites. Activates on keywords: Assist, voice control, wake word, intent, sentence, TTS, STT, Piper, Whisper, Wyoming.
esphome-config-helper
nodnarbnitram/claude-code-extensions
This skill should be used when the user asks to "create an esphome config", "set up an esp32 device", "configure esphome yaml", "add a sensor to esphome", "fix esphome compile error", or mentions "gpio pin assignment", "wifi setup", "ota update", or error messages like "Unknown platform", "GPIO already in use", "Could not compile", or "WiFi connection failed". Provides rapid ESPHome configuration generation, troubleshooting, and validation.
frigate-configurator
nodnarbnitram/claude-code-extensions
Open the skill to read its SKILL.md instructions.
ha-energy
nodnarbnitram/claude-code-extensions
Open the skill to read its SKILL.md instructions.
Popular frontend & design skills
setup-matt-pocock-skills
mattpocock/skills
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
hyperframes-animation
heygen-com/hyperframes
All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.
prototype
mattpocock/skills
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
vercel-react-best-practices
vercel-labs/agent-skills
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.