Scaffold a complete Home Assistant Custom Integration skeleton — manifest, lifecycle, config flow, coordinator, entity, platforms, translations, icons, diagnostics, plus pytest harness — in one go, conformant to every MUST pattern in spec/ha/*. Activate on phrasings like "scaffold a new HA Custom Integration", "create a Home Assistant integration", "neue HA-Integration scaffolden", "skeleton einer HA Custom Integration anlegen", "bootstrap a new HACS-compatible integration". Do not activate when the user only edits an existing integration, scaffolds a Lovelace card, scaffolds an ESPHome component, scaffolds a blueprint, or asks for a YAML-to-config-flow migration — those have their own skills.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-home-assistant:ha-integration-scaffoldThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Spec: <https://github.com/nolte/claude-home-assistant/blob/develop/spec/claude/ha-integration-scaffold/de.md> (DE canonical) / [`en.md`](https://github.com/nolte/claude-home-assistant/blob/develop/spec/claude/ha-integration-scaffold/en.md).
Spec: https://github.com/nolte/claude-home-assistant/blob/develop/spec/claude/ha-integration-scaffold/de.md (DE canonical) / en.md.
Use this skill when the user wants to:
spec/ha/* spec firstcustom_components/<domain>/ → there is no scaffold to do; consult the relevant detail spec under spec/ha/*ha-lovelace-card-scaffold (planned)ha-esphome-component-scaffold (planned)custom_components/<domain>/. If the directory exists, abort with the path quoted. Collision is a user-disambiguation problem, not a generator problem.hass.data[DOMAIN]. Every setup artefact lives in typed entry.runtime_data (see ha/runtime-data-pattern). The generated __init__.py is the single source of this contract._attr_name = "<string>" on a generated entity. All entity names live in strings.json under entity.<platform>.<translation_key>.name (see ha/translations). Hard-coded names break translation and stable entity_id slugs.self.entity_id = "...". HA derives the entity_id from the system-language display name at first registration. The skeleton stays English-source so the slug remains language-independent (see ha/entity-architecture).api.py carries _API_PATH_RE and a _with_auth(headers) helper; bearer tokens go on the wire only after the path has passed the whitelist (see ha/security-hardening).hacs / zeroconf / auth / platforms, fall back to documented defaults — but state every default in the output summary so the user sees what was assumed.| Field | Required | Default | Notes |
|---|---|---|---|
domain | yes | — | lowercase ASCII slug ([a-z0-9_]+); folder name, manifest.json:domain, DOMAIN constant |
name | yes | — | human-readable display name |
description | yes | — | 1–2 sentences for manifest.json and plan.md |
codeowner | yes | — | GitHub handle with @ prefix |
integration_type | yes | — | one of hub, device, service |
iot_class | yes | — | one of local_polling, local_push, cloud_polling, cloud_push, assumed_state, calculated |
target_dir | yes | — | repo-root path; the consumer must have a clean git repo here |
hacs | no | true | generate hacs.json |
zeroconf | no | false | generate async_step_zeroconf plus manifest.json:zeroconf |
auth | no | true | generate reauth flow |
platforms | no | ["sensor"] | list of HA platforms to scaffold |
If the user is silent on hacs, zeroconf, auth, or platforms, use the defaults but state them explicitly in the output.
git -C <target_dir> rev-parse --is-inside-work-tree returns true. If not, instruct the user to git init first.git -C <target_dir> status --porcelain returns empty. If the working tree is dirty, abort and tell the user to commit or stash first.<target_dir>/custom_components/<domain>/ does not exist. If it does, abort with the path quoted.<target_dir>/manifest.json (at repo root) does not exist. A root-level manifest.json indicates an HA add-on layout, not a Custom Integration — abort and explain.Resolve every input including defaults. Print a one-paragraph summary listing exactly what will be scaffolded:
sensor, binary_sensor, …)manifest.json:domain, integration_type, iot_classWait for user confirmation. This is the only approval gate — the rest of the workflow is bulk-write.
custom_components/<domain>/Write these files (every file is mandatory unless marked optional):
manifest.json — every required field per ha/integration-architecture__init__.py — async_setup_entry + async_unload_entry with runtime_data (ha/runtime-data-pattern)const.py — DOMAIN, PLATFORMS, CONF_*, DEFAULT_POLL_* / MIN_POLL_*api.py — API client skeleton with path whitelist + bearer gating (ha/security-hardening)config_flow.py — user flow + (when auth=true) reauth + reconfigure + options flow (ha/config-flow-patterns); when zeroconf=true, additionally async_step_zeroconf (ha/zeroconf-discovery)coordinator.py — <Domain>Coordinator with error mapping (ha/coordinator-patterns)entity.py — base entity + DeviceInfo factories (ha/entity-architecture, ha/device-registry)platforms (default: sensor.py)strings.json — English source (ha/translations)translations/en.json and translations/de.json — mirrors of strings.jsonicons.json — icon mappings (ha/icons)diagnostics.py — redaction hook with TO_REDACT (ha/diagnostics)services.yaml (optional, only when services are explicitly requested)hacs.json at the repo root (when hacs=true)Cross-file consistency invariants — verify before writing:
<domain> everywhere it appears__init__.py and every platform moduletranslation_key in strings.json, translations/<lang>.json, icons.json, and the EntityDescription instancesunique_id format string across every platform moduletests/Write:
tests/conftest.py — mock_config_entry_data + API mock fixture using load_fixture (ha/test-harness)tests/test_config_flow.py — user-flow happy path, user-flow sad path; reauth tests when auth=truetests/test_coordinator.py — ConfigEntryAuthFailed and UpdateFailed mappingstests/test_init.py — async_setup_entry / async_unload_entry lifecycletests/test_diagnostics.py — TO_REDACT redaction checktests/fixtures/health.json — sample API responsepytest.ini (or [tool.pytest.ini_options] if pyproject.toml exists) with asyncio_mode = autopytest-homeassistant-custom-component in requirements-dev.txt (if missing)Run, in this order, surface failures back to the user:
ruff check custom_components/<domain>/
pytest tests/ -v
Both must run without errors. If they fail, the generator produced inconsistent output — abort and quote the failing tool output.
plan.mdAt target_dir/plan.md, with these sections:
spec/ha/* requirementapi.py, populate EntityDescription tuples with real datapoints, augment tests/fixtures/)spec/ha/* Open Questions sectionsReturn a brief summary listing:
plan.mdhacs/action@main is configured in the project-structure scaffold)ha-config-flow-augment (planned)ha-coordinator-add (planned)ha-lovelace-card-scaffold (planned)ha-test-harness-augment (planned)ha-integration-deploy / ha-integration-verify (planned)nolte-shared:project-structure-applyGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub nolte/claude-home-assistant --plugin claude-home-assistant