Home AssistantHome AssistantContributors
On this page

An intent describes what someone wants, separate from how they said it. "Turn on the kitchen lights", "kitchen lights on", and a button on a voice assistant all become the same intent: HassTurnOn, with the slots area set to kitchen and domain set to light.

That split is the point. The thing that understands the user (Assist, a large language model, or an external voice assistant) produces an intent. The thing that acts on it, an intent handler, does not care where it came from. Write a handler once, and it works from every source.

In Home Assistant:

  • The conversation integration turns text into intents, with the sentences from the intents repository. See the conversation API.
  • The LLM API offers intents to large language models as tools. Not every intent: an integration picks which of its intents to offer.
  • The intent integration registers the built-in intents and offers an HTTP endpoint to fire them. It is loaded with conversation, which is part of the default configuration.
  • Any integration can register handlers for its own intents.

The intent object

Intent from homeassistant.helpers.intent holds:

Attribute Description
hass The Home Assistant instance.
platform The integration that fired the intent.
intent_type The name of the intent, like HassTurnOn.
slots The slot values, by slot name. Each slot is a dictionary with a value.
text_input The text that led to this intent, if any.
context The Context, with the user.
language The language, which defaults to the language of the instance.
assistant The assistant that is handling the request, like conversation. Used to only act on entities exposed to it.
device_id The device the request came from, like a voice satellite.
satellite_id The entity ID of the satellite the request came from.
conversation_agent_id The conversation agent that is handling the request.

Handle an intent

Subclass IntentHandler and register an instance with intent.async_register. An integration with intents usually does this in an intent.py platform, with an async_setup_intents function that the intent integration calls:

Python
import probatio

from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, intent

INTENT_ADD_COFFEE = "MyIntegrationAddCoffee"


async def async_setup_intents(hass: HomeAssistant) -> None:
    """Set up the intents of my integration."""
    intent.async_register(hass, AddCoffeeHandler())


class AddCoffeeHandler(intent.IntentHandler):
    """Count how much coffee was had."""

    intent_type = INTENT_ADD_COFFEE
    description = "Adds cups to today's coffee count"
    slot_schema = {probatio.Optional("cups"): cv.positive_int}

    async def async_handle(
        self, intent_obj: intent.Intent
    ) -> intent.IntentResponse:
        """Handle the intent."""
        slots = self.async_validate_slots(intent_obj.slots)
        cups = slots.get("cups", {}).get("value", 1)
        total = await async_add_coffee(intent_obj.hass, cups)

        response = intent_obj.create_response()
        response.async_set_speech(f"That is {total} cups today.")
        return response
  • intent_type is the name. Prefix your own intents to avoid clashes. Names starting with Hass belong to Home Assistant.
  • description explains the intent. When the intent is offered to language models as a tool, they read it, so write it for them.
  • slot_schema describes the slots. async_validate_slots validates them, and raises when they do not fit.
  • platforms optionally limits which integrations may fire the intent.

Speech you set in a handler is spoken as is, it is not translated. The built-in intents get their response sentences, in every language, from the intents repository instead.

For the common case of an intent that calls an action on matching entities, ServiceIntentHandler from homeassistant.helpers.intent does the matching, the calling, and the response for you.

The response

intent_obj.create_response() returns an IntentResponse:

  • async_set_speech(text): what to say back. Plain text by default, or SSML with speech_type="ssml".
  • async_set_reprompt(text): what to say if the user does not answer in time, for a response that asks a question.
  • async_set_card(title, content): a card for assistants that can show one.
  • async_set_results(success_results, failed_results): the targets that were acted on, as IntentResponseTarget objects.
  • async_set_error(code, message): turn the response into an error.
  • response_type: IntentResponseType.ACTION_DONE by default. Set it to QUERY_ANSWER for an answer to a question.

Raise intent.IntentHandleError when the intent cannot be handled, with a message for the user.

Fire an intent

If your code understands the user itself, like an integration for an external voice assistant, let Home Assistant handle the result:

Python
from homeassistant.helpers import intent

try:
    response = await intent.async_handle(
        hass,
        DOMAIN,
        intent.INTENT_TURN_ON,
        {"name": {"value": "Kitchen light"}},
        context=context,
        language="en",
    )
except intent.UnknownIntent:
    _LOGGER.warning("Unknown intent")
except intent.InvalidSlotInfo as err:
    _LOGGER.error("Invalid slot data: %s", err)
except intent.IntentError:
    _LOGGER.exception("Error handling the intent")

It is up to you to bring the response back to the user. response.speech holds the speech by type, like response.speech["plain"]["speech"]. response.as_dict() gives the whole response, in the same shape as the conversation API.

Over HTTP

The intent integration offers POST /api/intent/handle, with the intent name and its slot values in data:

JSON
{
  "name": "HassTurnOn",
  "data": {
    "name": "Kitchen light"
  }
}

Optional fields are language, assistant, device_id, and satellite_id. The response is the intent response as JSON.

To send text instead of an intent, and let Home Assistant work out the intent, use the conversation API.

Was this page helpful?

Last updated on .