Claude CodeでHome Assistantのカスタムコンポーネントを作る
Home Assistantのカスタムコンポーネントはmanifest.jsonとhacs.jsonの必須キーが細かく決まっています。CLAUDE.mdとhooksで漏れを防ぐ手順をまとめます。
Home Assistantのカスタムコンポーネント(custom integration)は、custom_components/配下に置くmanifest.jsonの必須キーと、HACS配布用のhacs.jsonのキーがそれぞれ細かく決まっています。どちらも1つ抜けるだけで読み込みに失敗したり、HACSへの登録が弾かれたりします。Claude Codeにこの2つのファイルを生成・点検させるときの押さえどころをまとめます。
Home Assistantのカスタムコンポーネントとは何か
Home Assistant本体に組み込まれた「コア統合」に対して、ユーザーがcustom_components/ディレクトリに追加する統合を「カスタムコンポーネント」または「カスタム統合」と呼びます。動作の仕組み自体はコア統合と同じで、manifest.jsonでメタ情報を宣言し、__init__.pyでエントリのセットアップを行います。
違いが出るのは配布と検証の場面です。コア統合はHome Assistant本体のPRレビューを通りますが、カスタム統合は開発者自身がJSON構文と必須キーの整合性に責任を持ちます。Home Assistant Community Store(HACS)経由で配布する場合は、リポジトリ構成にも決まった型があります。この「決まった型を機械的に満たす」作業は、Claude CodeにCLAUDE.mdでルールを与えて反復させるのに向いています。
開発前の前提とディレクトリ構成
Claude Codeがまだ入っていない場合は、先に導入します。macOSでの選択肢はClaude Code Macインストールにまとめています。
カスタムコンポーネントのファイルは、すべてcustom_components/<ドメイン名>/の下に置きます。HACSの公式ドキュメントは、1リポジトリにつき統合を1つだけ置く構成を要求しており、これを満たさないリポジトリは最初のサブディレクトリしか認識されません。
良い例と悪い例は次のとおりです。
# OK
custom_components/awesome/__init__.py
custom_components/awesome/sensor.py
custom_components/awesome/manifest.json
README.md
hacs.json
# NG(1): custom_components配下にない
awesome/__init__.py
awesome/manifest.json
awesome/hacs.json
# NG(2): ルート直下にファイルを置いている
# (hacs.jsonでcontent_in_root: trueを明示すれば例外的にOK)
__init__.py
manifest.json
hacs.jsonhacs.jsonはリポジトリのルートに置きます。manifest.jsonはcustom_components/<ドメイン名>/の中です。この2つを混同すると、HACS側では規約違反になり、Home Assistant側ではドメインとディレクトリ名の不一致でロードに失敗します。
manifest.jsonの必須キーをClaude Codeに徹底させる
manifest.jsonはHome Assistant本体が読む設定ファイルで、公式ドキュメントは以下のキーを説明しています。
| キー | 必須か | 内容 |
|---|---|---|
domain | 必須か必須 | 内容ディレクトリ名と一致する短い識別子。変更不可 |
name | 必須か必須 | 内容UIに表示される名前 |
version | 必須かカスタム統合は必須 | 内容コア統合では省略。AwesomeVersion準拠(SemVerやCalVer)の文字列 |
documentation | 必須かHACS登録時は必須 | 内容ドキュメントのURL |
issue_tracker | 必須かHACS登録時は必須 | 内容Issueの報告先URL |
codeowners | 必須かHACS登録時は必須 | 内容GitHubユーザー名の配列 |
requirements | 必須か任意 | 内容pip形式の依存パッケージ配列 |
integration_type | 必須か任意(推奨) | 内容hub / device / serviceなど。未指定時はhub扱い |
iot_class | 必須か任意 | 内容local_pollingやcloud_pushなど通信方式の分類 |
config_flow | 必須か任意 | 内容trueにする場合config_flow.pyが必須 |
HACS側のドキュメントは、登録対象の統合リポジトリについて「domain / documentation / issue_tracker / codeowners / name / versionの6つを最低限定義すること」と明記しています。ここが抜けると、Home Assistant単体では動いてもHACSには登録できません。
CLAUDE.mdに次のような節を足しておくと、Claude Codeがmanifest.jsonを書くたびにこの6キーを見落としにくくなります。
## manifest.json
- 新規のカスタム統合を作るときは、次の6キーを必ず埋める:
`domain` / `name` / `version` / `documentation` / `issue_tracker` / `codeowners`
- `domain`は`custom_components/`配下のディレクトリ名と完全一致させる
- `version`はSemVer形式(例: `1.0.0`)で書く。コア統合と違いカスタム統合では省略できない
- `config_flow: true`を書いたら`config_flow.py`が存在することを確認する「フォーマットを守って」のような曖昧な指示ではなく、キー名を名指しする書き方にすると、Claudeが省略していいかどうかで迷う余地が減ります。
integration_typeとiot_classで統合の性格を宣言する
integration_typeは、その統合が何を提供するかを表すキーです。値は次の8種類です。
| 値 | 意味 |
|---|---|
device | 意味ESPHomeのような単一デバイスを提供 |
entity | 意味sensorやlightなど基本的なエンティティプラットフォーム(通常は使わない) |
hardware | 意味Raspberry Piなどのハードウェア統合(通常は使わない) |
helper | 意味input_booleanやderivativeなど、自動化を助けるエンティティ |
hub | 意味Philips Hueのように複数デバイス・サービスへのゲートウェイになる統合 |
service | 意味DuckDNSやAdGuardのように単一サービスを提供 |
system | 意味システム統合用に予約(通常は使わない) |
virtual | 意味実体を持たず、別の統合やIoT標準を指し示すだけの統合 |
コア統合でconfig_flowを持つ場合integration_typeは必須ですが、カスタム統合とYAMLベースの統合では未指定時にhub扱いになります。ただし公式ドキュメントは、未指定に頼らず明示することを推奨しています。
iot_classは通信方式の分類です。assumed_state(状態を取得できず直前のコマンドから推測)、cloud_polling、cloud_push、local_polling、local_push、calculated(通信せず計算結果だけを提供)の6種類があります。ローカルデバイスに直接ポーリングでつなぐ典型的なカスタム統合ならlocal_polling、プッシュ通知が来るならlocal_pushを選びます。
Claude Codeに統合の雛形を作らせるときは、対象デバイスがローカルかクラウド経由か、プッシュ通知に対応しているかを先に伝えておくと、integration_typeとiot_classを実態に合わせて埋めてくれます。
依存パッケージをローカルで試す
requirementsキーに書いたPythonパッケージは、Home Assistant起動時に自動インストールされます。開発中に未公開のバージョンや自分でパッチしたバージョンを試したいときは、公式ドキュメントが次の手順を案内しています。
pip install pychromecast==3.2.0 --target ~/.homeassistant/deps
hass --skip-pip-packages pychromecast--skip-pip-packagesを付けると、指定したパッケージについてはrequirementsの内容で上書きされなくなります。依存パッケージ自体を修正しながら試したい場合は、pip install -eで開発版をリンクする方法も使えます。GitHubの特定ブランチやコミットをrequirementsに直接指定することもでき、"pycoolmaster@git+https://github.com/issacg/pycoolmaster.git@except_connect"のような書き方でブランチを固定できます。
カスタム統合のrequirementsには、Home Assistant本体のrequirements.txtに既に含まれているパッケージを重複して書かないよう公式ドキュメントが注意しています。
DataUpdateCoordinatorでポーリングを実装する
外部APIやローカルデバイスからデータを取る方式は、大きくpush型とpoll型に分かれます。push型はAPI側から変化を通知してもらう方式で、poll型は一定間隔でこちらから取りに行く方式です。Home Assistantは複数のエンティティが同じAPIエンドポイントをまとめて叩く場合に備えて、DataUpdateCoordinatorというクラスを提供しています。
公式ドキュメントの例に沿った形(実際の出力ではなく設計の下敷きとして)を示すと、次のような構造になります。
class MyCoordinator(DataUpdateCoordinator):
"""My custom coordinator."""
def __init__(self, hass, config_entry, my_api):
super().__init__(
hass,
_LOGGER,
name="My sensor",
config_entry=config_entry,
update_interval=timedelta(seconds=30),
always_update=True,
)
self.my_api = my_api
async def _async_update_data(self):
try:
async with asyncio.timeout(10):
return await self.my_api.fetch_data()
except ApiAuthError as err:
raise ConfigEntryAuthFailed from err
except ApiError as err:
raise UpdateFailed(f"Error communicating with API: {err}") from erralways_updateは既定でTrue扱いです。取得データが前回と同じ値でも毎回リスナーへ通知が飛ぶため、APIのレスポンスが__eq__で比較できる形ならalways_update=Falseにして不要な通知を減らせます。
Claude Codeにこの雛形を書かせるときは、「DataUpdateCoordinatorを継承したクラスを作って」だけでなく、ポーリング対象のAPIがpushかpollか、更新間隔は何秒か、認証エラー時にConfigEntryAuthFailedを投げる必要があるかを具体的に伝えると、汎用の雛形止まりにならずに済みます。エンティティ側はCoordinatorEntityを継承し、_handle_coordinator_updateでself.coordinator.dataを読んで状態を書き込む形が公式の基本形です。
hacs.jsonでHACS配布向けに公開する
HACSに登録するには、リポジトリ自体がいくつかの前提を満たしている必要があります。公開リポジトリであること、GitHubリポジトリにDescription(用途を短く説明する文)とTopics(検索用のタグ)が設定されていること、使い方を書いたREADMEがあることの3つです。DescriptionはHACS上の説明文に使われ、Topicsは表示されないもののHACSストアでの検索に使われます。
そのうえで、リポジトリのルートにhacs.jsonを置きます。公式ドキュメントが挙げているキーは次のとおりです。
| キー | 型 | 必須 | 内容 |
|---|---|---|---|
name | 型string | 必須必須 | 内容HACSのUIに表示される名前 |
content_in_root | 型bool | 必須任意 | 内容統合本体をリポジトリ直下に置く場合true |
zip_release | 型bool | 必須任意 | 内容GitHub Releasesでzip配布する場合に指定(filenameと併用) |
filename | 型string | 必須任意 | 内容HACSが探すファイル名(単一ファイル形式の場合) |
hide_default_branch | 型bool | 必須任意 | 内容デフォルトブランチをダウンロード対象から外す |
country | 型string | 必須任意 | 内容ISO 3166-1 alpha-2形式の国コード |
homeassistant | 型string | 必須任意 | 内容対応するHome Assistantの最低バージョン |
hacs | 型string | 必須任意 | 内容対応するHACS自体の最低バージョン |
persistent_directory | 型string | 必須任意 | 内容アップグレードで消さないディレクトリ(統合限定) |
最小構成の例です。
{
"name": "My awesome thing",
"content_in_root": false,
"homeassistant": "2024.1.0"
}これに加えて、HACSはブランド用アイコン(icon.pngを含むbrandディレクトリ)の同梱を求めています。GitHub Releasesの発行は必須ではありませんが、発行しておくとHACSのダウンロード画面で最新5件のリリースから選べるようになります。発行しない場合、HACSはデフォルトブランチの内容をそのまま使います。
CLAUDE.mdとhooksでmanifest/hacs.jsonの整合を守らせる
必須キーの一覧をCLAUDE.mdに書くだけでは、JSON構文のtypoまでは防げません。カンマの付け忘れや閉じ括弧の欠落は、Home Assistant起動時に初めて気づくことが多く、原因の特定に時間を取られます。
settings.jsonのPostToolUse hookで、manifest.jsonとhacs.jsonを編集した直後にJSON構文だけを機械的に検証できます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "f=$(jq -r '.tool_input.file_path'); case \"$f\" in *manifest.json|*hacs.json) python3 -m json.tool \"$f\" >/dev/null || { echo 'JSON構文エラー: '\"$f\" >&2; exit 2; };; esac"
}
]
}
]
}
}hookはstdinでJSONを受け取り、Edit/Writeが対象にしたファイルパスはtool_input.file_pathに入っています。jqで取り出し、manifest.jsonまたはhacs.jsonのときだけpython3 -m json.toolで構文チェックします。構文エラーのときはexit 2で終了し、あわせてstderrにメッセージを出します。PostToolUse hookはexit 2でないとClaudeにstderrの内容が渡らないため、この形にしないと構文エラーがあってもClaudeに気づかれずに終わります。hookの仕組みとPostToolUseの使い方はPostToolUse hookでツール実行後の後処理を自動化するに詳しくまとめています。
Home Assistant coreのDevelopment Checklistでは、CODEOWNERSへの追加をpython3 -m script.hassfestで行うと案内されています。これはHome Assistant core本体への貢献(コントリビュート)を前提にした手順で、カスタム統合を新規に作る場面で必須のステップではありません。手元でより厳密な検証をしたくなったときの選択肢として、こういうツールがあることだけ押さえておけば十分です。
CLAUDE.mdに規約を書いてClaudeに守らせる考え方そのものは、テスト規約を教える場合と同じです。pytestプロジェクトでの具体例はClaude CodeにpytestのCLAUDE.md規約を教えるを参照してください。
よくあるつまずき
- 1リポジトリに統合を2つ以上置く: HACSは最初のサブディレクトリしか管理しないため、2つ目以降は登録から漏れます
domainとディレクトリ名の不一致:manifest.jsonのdomainはcustom_components/配下のディレクトリ名と一致している必要がありますversionの書き忘れ: コア統合のチュートリアルを参考にすると省略しがちですが、カスタム統合では必須です- コア側の
requirements.txtと重複する依存を書く: 公式ドキュメントは、Home Assistant本体のrequirements.txtに含まれるものを重ねて書かないよう求めています - ブランドアイコンの同梱漏れ:
brandディレクトリとicon.pngが無いと、HACS登録の要件を満たしません - リポジトリのDescription・Topics・README未設定:
hacs.jsonの中身がどれだけ正しくても、GitHubリポジトリ自体にDescription・Topics・READMEが無いとHACSにリポジトリとして追加する条件を満たせません hacs.jsonの置き場所の勘違い:hacs.jsonはリポジトリルート、manifest.jsonは統合ディレクトリの中です。取り違えるとどちらの検証も通りません
Claude Codeでの検証をどう組み立てるか
すべての検証を毎回フルセットで回す必要はありません。作業内容に応じて使い分けます。
| 検証内容 | おすすめ度 | 理由 |
|---|---|---|
JSON構文チェック(python3 -m json.tool) | おすすめ度◎ | 理由hookで自動化しやすく、コストがほぼゼロ |
| Home Assistantを実際に起動して読み込み確認 | おすすめ度◎ | 理由domain不一致や依存関係の欠落はここで初めて表面化する |
script.hassfest | おすすめ度△ | 理由Home Assistant core本体への貢献を前提にしたツールで、カスタム統合単体の開発では必須ではない |
| HACSのカスタムリポジトリ登録でのテストインストール | おすすめ度◎ | 理由配布時のhacs.json不備は実際にHACSへ登録してみないと分からない |
JSON構文チェックはhookで機械的に、Home Assistantでの起動確認とHACSでのテストインストールは節目ごとに手動で、という組み合わせが現実的です。
まとめ
Home Assistantのカスタムコンポーネント開発では、manifest.jsonの必須6キー(domain / name / version / documentation / issue_tracker / codeowners)と、hacs.jsonのnameをはじめとするキー群、そしてリポジトリ構成(1リポジトリ1統合・custom_components/<ドメイン名>/配下)という3つの決まりごとを同時に満たす必要があります。CLAUDE.mdにキー名を名指しで書き、PostToolUse hookでJSON構文を機械チェックする組み合わせにすると、Claude Codeに任せたときの手戻りを減らせます。DataUpdateCoordinatorのようなボイラープレートも、公式の実装パターンを具体的に伝えたうえで書かせると、汎用的すぎる雛形で止まらずに済みます。