# Home Assistant → Homarr Link This peer follows the [Homarr ownership and compatibility contract](homarr-integration-contract.md). The reference owner must register `homeAssistantTarget()` in `internal/apps/homarr/targets.go`; this peer does not edit the common registry. ## Source baseline, not live acceptance - Homarr API `2.4.1`, resolved OCI source `3d4150e59f600876b7e308f857b2f21572b75348` inspected locally read-only. The tagged source `ebbd4876d25a51151444a009ab596e1154d62880` remains a separate research baseline. - Original peer history forward-merges SDK `0bf67c3c64c229af25f19c1b93264f19cc57a910`; no history rewrite. - Home Assistant `2026.9.3`, image `ghcr.io/home-assistant/home-assistant:2026.9.3`. - Both the authenticated `/api/config` version and the WebSocket welcome version must match the supported Home Assistant version. - No Docker or browser run was made in this peer lane. Resolved image digests, rendered widget acceptance and the contained lifecycle proof remain pending the parent's approved disposable Project batch. Fake tests are not live proof. ## Account and token Owner creation keeps the existing official `/api/onboarding/users` flow. Its authorization code is not a usable Homarr credential. The login flow `/auth/login_flow` and `/auth/login_flow/` yields an authorization code; form-encoded `/auth/token` exchanges that code for a temporary access token and an OAuth refresh token. Neither is saved as Homarr's API key. Temporary refresh tokens are revoked through `/auth/revoke` when the helper finishes, including on failure. Through the official `/api/websocket` API, Setup lists users with `config/auth/list`, creates a dedicated `self-host Homarr` identity with `config/auth/create`, and attaches the `self-host-homarr` login using `config/auth_provider/homeassistant/create`. Its sole group is `system-read-only`, with `local_only=true`; it is not an owner or administrator. The owned user ID and generated password use `h.SetSecret`, not database access. An existing name/username without recorded ownership is not adopted. Changed groups, missing users/passwords, MFA and rejected authentication remain Manual. No user's password, group, token, device or automation is reset. After signing in as that dedicated user, `auth/long_lived_access_token` creates the token named `self-host Homarr`, valid for 365 days. `auth/current_user` and `auth/refresh_tokens` verify the current token's exact identity, name, type and identifier. Setup stores `HOME_ASSISTANT_HOMARR_TOKEN` and its ID through the Project secret boundary; a valid token is reused. A named token whose value was lost is not silently replaced: restore it, or delete only that named token in the dedicated user's profile and retry `self-host setup home-assistant`. The owner can inspect the dedicated identity in Settings → People → Users. The helper runs through `h.Exec` in the App container and uses its already shipped Python/aiohttp dependency (Home Assistant pins aiohttp `3.14.3`). Credentials travel only in the helper environment and HTTP/WebSocket bodies, never command arguments. Captured token output is registered for redaction before storage or error handling; raw response bodies and tracebacks are not printed. No Go dependency, host mount, device privilege or socket is added. Browser-only recovery stays Manual. Sign in as the dedicated user and issue `self-host Homarr` under Profile → Security → Long-Lived Access Tokens, then import it through the CLI private-input path: ```sh self-host install home-assistant --set-file home-assistant.homarr_token=- --yes self-host setup home-assistant self-host setup homarr ``` Enter the value privately on stdin; never put it in a command argument or chat. An imported token still must pass identity, group and token-type verification. Owner MFA is not disabled or bypassed; when it blocks that verification, Setup continues to report Manual rather than claiming unattended readiness. Restore a lost dedicated password or identity record from a Backup. ## Real entity and widget data The native kind is `homeAssistant`, with one `apiKey` secret containing the verified long-lived access token. Homarr uses `http://home-assistant:8123` server-side; the app tile separately uses the browser Address. The reference's peer preflight runs the App's fresh Check before compatibility requests or any Homarr tile/integration/widget mutation, and prints an actionable recovery hint on unready prerequisites. Removal does not require that preflight, so an unavailable peer cannot block owned cleanup. Authenticated `/api/states` discovery selects an existing available `sun.*` or `input_text.*` entity, deterministically, and records its returned ID. It never assumes `sun.sun` exists. If neither exists, the person explicitly selects an actual safe entity or creates a harmless text helper in Home Assistant. For example, replacing the placeholder with its actual ID: ```sh self-host install home-assistant --set home-assistant.homarr_entity_id= --yes self-host setup homarr ``` `/api/states/` must return that entity, an available state, attributes and valid change/update timestamps. An empty discovery list is a valid empty dataset but cannot satisfy Entity State acceptance; unknown or unavailable data remains Manual, not a fabricated state or an auth success. The widget is `smartHome-entityState`, limited to one integration, with options `entityId`, `displayName`, `entityUnit`, and `clickable=false`. It reads a state; it cannot toggle anything. Verification reads Homarr's `widget.smartHome.entityDetails` and requires the real selected identity, available state, attributes and parsed ordered timestamps, not just the saved widget. Separate volatile states are not compared for equality. Shared Stats binds `entities` and `unavailable`; central `verifyData` reads the catalog and acquires one forced advancing refresh. The pure `verifyHomeAssistantStats` callback checks that same sample's count units, finite nonnegative integers, at least one entity and unavailable no greater than total entities. Legitimate zero unavailable entities is accepted. Native failures stop before Stats acquisition. Focused descriptor HTTP fixtures exercise the real `verifyData` path; they remain source validation, not runtime or rendered acceptance. Neither Calendar nor Execute Automation is enabled: no calendar or safe automation identifier has been selected. Those features would need their own selected identifiers and explicit action approval. The common contract preserves owned integration/widget IDs and unrelated resources. A selected-entity change or an enabled toggle in an already owned Entity State widget is reported as Manual, with an instruction to edit only that widget in place and disable clickable. The common seam does not yet reconcile dynamic binding options, so the peer does not silently rewrite them. Removal uses only the common recorded ownership and shared-binding cleanup. ## Remaining Manual boundaries Home Assistant's `user`, `core_config`, `analytics` and `integration` onboarding steps must all be complete, or its completed-onboarding route must be removed. Core-config completion starts upstream provider flows and remains a person's choice. Setup never submits it, scans the network, adds hardware, triggers an automation or uses a real person's Project for acceptance. The pending parent batch must establish both installation orders, late peer readiness/retry, rerun/restart, credential updates, removal/reinstallation, user-resource preservation and at least one rendered result from a controlled entity through the registered descriptor. None of these live outcomes is claimed by source implementation or API-shaped fixtures. ## Official sources - [Homarr Home Assistant guide](https://homarr.dev/docs/integrations/home-assistant/) - [Exact Homarr adapter](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/homeassistant/homeassistant-integration.ts) - [Exact entity widget options](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/widgets/src/smart-home/entity-state/index.ts) - [Exact native entity routes](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/widgets/smart-home.ts) - [Exact Stats metrics](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/stats/existing.ts) - [Home Assistant authentication API](https://developers.home-assistant.io/docs/auth_api/) - [Home Assistant REST API](https://developers.home-assistant.io/docs/api/rest/) - [Home Assistant user API](https://github.com/home-assistant/core/blob/2026.9.3/homeassistant/components/config/auth.py) - [Home Assistant login credential API](https://github.com/home-assistant/core/blob/2026.9.3/homeassistant/components/config/auth_provider_homeassistant.py) - [Home Assistant token API](https://github.com/home-assistant/core/blob/2026.9.3/homeassistant/components/auth/__init__.py) - [Home Assistant onboarding boundaries](https://github.com/home-assistant/core/blob/2026.9.3/homeassistant/components/onboarding/views.py) - [Image's existing dependencies](https://github.com/home-assistant/core/blob/2026.9.3/pyproject.toml)