EntangledHome bridges Home Assistant, a prompt-adapter FastAPI microservice, and a Qdrant vector store to turn natural language requests into guarded service calls.
Status: integration scaffold. Wire your adapter service and Qdrant collections to unlock full functionality.
flowchart LR
HA[Home Assistant Conversation Agent] -->|utterance| Adapter[Prompt Adapter Service]
Adapter -->|catalog sync| Qdrant[(Qdrant Collections)]
Qdrant -->|top-k context| Adapter
Adapter -->|intent JSON| HA
HA -->|guardrails + service calls| HAAPIs[Home Assistant Domains]
- Home Assistant captures a free-form utterance and forwards it to the adapter service.
- The adapter enriches the prompt with catalog slices retrieved from Qdrant, validates the model output against the intent schema, and emits structured JSON.
- Home Assistant checks guardrails (secondary signals, confidence thresholds) before routing the action to the relevant domain service call.
- In Home Assistant, open HACS → Integrations, then select the overflow menu (⋯) and choose Custom repositories.
- Paste the repository URL and set the category to Integration so HACS indexes the metadata
published in
hacs.json. - Search for EntangledHome - Assistant under HACS Integrations and click Download to install the integration.
- Restart Home Assistant once HACS finishes copying files into
custom_components/entangledhome/.
Troubleshooting tips:
- If the repository does not appear, confirm the URL is correct and the category is set to Integration before saving it in HACS.
- After installation, verify that
custom_components/entangledhome/icon.svgandcustom_components/entangledhome/logo.pngexist. Reinstall the integration from HACS if assets are missing, as HACS caches downloads per version tag. - Should HACS report validation warnings, rerun
python3 -m script.hassfestlocally to spot manifest or metadata issues before retrying the installation.
This integration is configured exclusively via Home Assistant's config flow; YAML configuration is not supported. Use the options flow to manage guardrails, adapter credentials, and catalog synchronization.
- Copy the repository (HACS custom repository or manual) into
custom_components/. - In Home Assistant, navigate to Settings → Devices & Services → + Add Integration and search for EntangledHome - Assistant.
- When prompted, provide the adapter URL, Qdrant host, and optional Qdrant API key. The
docs/examples/homeassistant_configuration.yamlfile lists each field exposed in the form along with its default value for quick reference when onboarding via the UI. - After the entry is created, open Configure on the integration card to review options such as
catalog synchronization, guardrails, latency budgets, and Plex syncing. Home Assistant persists
these selections in
.storage; re-open Configure whenever you need to adjust them. - Restart Home Assistant and confirm the integration loads without warnings when prompted by the UI.
- Provision two collections:
ha_entities(Home Assistant areas/devices/scenes) andplex_media(media catalog). - Each collection should be created with a cosine metric, vector size matching your embedding
model (e.g., 384 for
text-embedding-3-smallequivalents), and payload schemas described inadapter_service/README.md. - Configure Qdrant API keys and network policies so only Home Assistant and the adapter can access the collections.
- Seed initial payloads by exporting Home Assistant registries and Plex metadata before enabling live sync jobs.
Fine-tune the adapter handshake per intent by editing the intents_config mapping. The mapping is
limited on purpose so the adapter receives just enough context to shape utterances before guardrail
enforcement happens inside Home Assistant.
intents_config supports the following keys:
enabled– set tofalseto stop forwarding an intent to the adapter without deleting it.slots– optional mapping of slot names to hint lists the adapter can surface as synonyms or canonical values.threshold– float (0–1) that nudges the adapter to demand more or less confidence for a given intent.
Use the guardrail options in the options flow—Dangerous intents (dangerous_intents),
Intent thresholds (intent_thresholds), Intent allowed hours (intent_allowed_hours),
Intent recent command windows (intent_recent_command_windows), and Intent secondary signals
(intent_secondary_signals)—to configure execution behavior that lives outside the adapter mapping
(enabled, slots, threshold).
Per-intent guardrail overrides such as intent_thresholds, intent_allowed_hours,
intent_recent_command_windows, and intent_secondary_signals run after intents_config is sent
to the adapter. Set threshold when you want the adapter to be more cautious; use the guardrail
overrides when Home Assistant itself must block, delay, or demand secondary signals even if the
adapter approves the intent.
Home Assistant stores EntangledHome settings inside .storage/core.config_entries; the integration
never reads from configuration.yaml. Use the examples under docs/examples/ as worksheets while
filling out the config flow and options flow:
docs/examples/homeassistant_configuration.yamlenumerates each field exposed during setup and in the options dialog along with default values, validation hints, and behavioral notes.docs/examples/intents.yamlcaptures a representativeintents_configobject. Paste an adapted JSON version of this mapping into the Intent routing configuration field of the options flow to seed your own guardrails and slot hints.
- Navigate to Settings → Devices & Services → Integrations → EntangledHome - Assistant.
- Choose Configure to open the options flow and scroll to Intent routing configuration.
- Paste a JSON object mirroring the YAML example above, including keys such as
enabled,slots, andthreshold. - Use the guardrail fields in the same dialog (for example Dangerous intents, Intent thresholds, Intent allowed hours, Intent recent command windows, and Intent secondary signals) to configure execution policies that should not live inside the adapter mapping.
- Submit the form and reload the integration. The adapter receives the updated mapping on the next utterance, and the guardrail overrides apply immediately on the Home Assistant side.
Sensitive intents (unlocking doors, opening garages, disarming alarm panels) use the adapter’s
required_secondary_signals contract. The integration provides two built-in signals:
presence– satisfied when configuredperson.*entities reporthome. The provider also emits entity-scoped tokens such aspresence:person.alicefor fine-grained policies.voice– satisfied when a recent voice profile match is stored. The helper writes tokens such asvoice:alicewith a configurable TTL.
Additional safeguards:
- Confidence gating: set via options to require a minimum adapter confidence score.
- Latency watchdog: configure the Latency budget (ms) option to warn when the adapter takes longer than expected to respond.
- Intent allow/block lists: restricts which service domains the adapter may invoke.
- Signature checks: if enabled, Home Assistant signs requests to the adapter and validates the response signature documented in the adapter README.
- Sensitive intent double-confirmation: downstream automations can prompt users for follow-up confirmations before executing irreversible actions.
- Verified user enforcement: enable Require verified user for dangerous intents and populate the
Verified users allow list (comma separated or JSON array). The adapter must return a
verified_uservalue that matches one of the configured names before dangerous intents execute.
Guardrail metadata can be edited globally or per intent:
- Confidence threshold – set the global gate in the options flow or override individual
intents via the Intent thresholds field (
intent_thresholds). The adapter still receives thethresholdvalue fromintents_config, but Home Assistant ultimately enforces theintent_thresholdsoverride if one is provided. - Dangerous intents – list intent IDs in the Dangerous intents field (
dangerous_intents). Pair dangerous entries with Intent secondary signals (intent_secondary_signals) and Intent allowed hours (intent_allowed_hours) to require presence/voice tokens or suppress the intent overnight. - Require verified user for dangerous intents – when enabled, the integration cross-checks the
adapter’s
verified_userfield against the Verified users allow list before executing. - Recent command window overrides – widen or shrink deduplication for specific intents via the
Intent recent command windows field (
intent_recent_command_windows). - Latency budget – set the global adapter round-trip target (default 2000 ms). When exceeded,
Home Assistant emits a warning log and tags telemetry events with
latency_budget_exceededso you can alert on degraded performance.
When a dangerous intent executes, the integration annotates telemetry payloads with
dangerous: true so downstream automations can log or audit critical invocations.
- Install development dependencies with
scripts/setup_env.shandsource .venv/bin/activate. - Run static analysis:
ruff check
- Execute the full pytest suite (includes documentation guardrails):
pytest
- For adapter service changes, run the FastAPI app locally and exercise
POST /interpretusing the catalog fixtures stored intests/stubs/.
Continuous integration fails when documentation sections or example assets drift from the
requirements enforced in tests/test_documentation.py, ensuring README guidance stays accurate.
The integration ships conversation sentence templates for each supported intent and a fallback
catch-all intent. Home Assistant loads the packaged defaults from
custom_components/entangledhome/sentences/en/. To provide sentence overrides, drop a file with the
same intent name under your Home Assistant configuration at
config/custom_components/entangledhome/sentences/en/<intent>.yaml. On setup the integration
prefers these sentence overrides while continuing to expose the packaged defaults for intents that
do not have a local template. The catch-all intent remains active regardless of overrides so free
form commands continue to flow to the adapter service.
Review docs/examples/sentences.en.yaml for a sample
catch-all override with localized slot hints. The integration reloads sentence overrides whenever
the config entry restarts, so edits take effect after a single reload. Pair these overrides with
the intents configuration to steer the adapter toward the vocabulary your household uses.
Secondary signals are controlled via config entry options (UI or YAML overrides):
| Option key | Description |
|---|---|
secondary_signals_presence_enabled |
Enable checks against person.* entities. |
secondary_signals_presence_entities |
List of entity IDs that prove presence. |
secondary_signals_voice_enabled |
Enable recent-voice detection guardrail. |
secondary_signals_voice_ttl_seconds |
Validity window for voice matches (default 30s). |
Voice profiles can be recorded by calling
record_voice_identifier(hass, entry_id, voice_id) from
custom_components.entangledhome.secondary_signals. Integrations that process STT events can store
voice hits there so the guardrail becomes satisfied for the next few seconds.
The scripts/ package contains helpers for bootstrapping Qdrant collections:
scripts/ingest_entities.py– Exports Home Assistant area/device/scene metadata using the websocket API and upserts the payloads into theha_entitiescollection.scripts/ingest_plex.py– Walks the Plex API and stores movies, shows, and music entries inplex_media.scripts/_qdrant.py– Shared utilities for creating collections with the expected schema.
Run these scripts from a virtual environment (uv run python scripts/ingest_entities.py) or bake
them into scheduled automations so the adapter receives fresh context.
- Provision environment variables documented in
adapter_service/README.mdso the FastAPI service knows how to reach Qdrant and downstream model APIs. - Deploy the adapter service close to Home Assistant (LAN or the same docker network) and
expose
POST /interpret. - Ensure the service validates all outputs against the published JSON schema before replying to Home Assistant.
- Configure TLS or reverse-proxy authentication when the adapter runs off-box.
- Containerized deployments: build and push a new adapter image, then restart the stack.
- Bare-metal installs: pull the repository, run
uv sync, and restart the FastAPI app. - During upgrades, monitor the adapter logs for JSON schema validation warnings – these signal prompt drift that requires regeneration or fine-tuning.
See docs/migration.md for a running log of schema changes, deprecated
options, and upgrade strategies. Each release entry describes:
- Components that changed and any required Home Assistant configuration updates.
- Guardrail defaults that moved or were renamed.
- Qdrant payload adjustments that require backfilling vector metadata.
If the integration fails to load or the adapter returns errors, start with
docs/troubleshooting.md. The guide covers:
- Common issues such as missing credentials or stale catalog caches.
- Adapter connectivity debugging with
uv run scripts/ingest_entities.py --dry-runand HTTP tracing. - Qdrant ingestion problems, including schema mismatch and rejected payloads.
- Guardrails – Secondary signal defaults now live in the options flow so dangerous intents inherit the tighter confirmation paths without manual YAML edits.
- Intent configuration – The adapter receives an
intentspayload with every utterance, aligning YAML snippets, UI configuration, and the FastAPI contract. - Adapter – Request/response validation tightened alongside signature configuration guidance to keep the prompt-adapter handshake secure.
- Qdrant – Updated ingestion scripts populate richer payload metadata for both
ha_entitiesandplex_mediacollections, improving retrieval context.
Full release notes: docs/releases/v0.5.0.md