# Home Assistant

Source: https://heyparlour.app/docs/home-assistant

> Control your house through Home Assistant, reach its Assist, use Parlour as its voice, and mute it.

Home Assistant gives Parlour tools for your house, a line in the system
prompt, and a mute switch. It is on by default and lives under
`integrations["home-assistant"]` in your config:

```json
{
  "integrations": {
    "home-assistant": {
      "url": "http://homeassistant.local:8123",
      "mcp": true,
      "rest": true,
      "assist": false,
      "language": "",
      "muteEntity": ""
    }
  }
}
```

The token is `HA_TOKEN` in `secrets.env`. It is a long-lived access token from
your profile page in Home Assistant, at the bottom of the Security tab. That
token controls the whole house, so it stays in `secrets.env` with mode 600
and never goes into git.

## Set it up

`parlour init` has a section just for Home Assistant. Four things can go
wrong, and they all look the same from the outside: the wrong address, no
token, a token for something else, or the MCP Server integration not yet
added. So `init` checks each answer as you give it:

1. **The address.** It tries the one in your config, then
   `homeassistant.local:8123`, `homeassistant:8123` and `localhost:8123`. It
   takes the first that answers the way Home Assistant does: `401` from
   `/api/` without a token, which a router's login page does not. If nothing
   is found, you type it in, and a wrong one is not quietly accepted.
2. **The token.** It prints the page to make one on
   (`<your url>/profile/security`), reads it without echoing it, and checks it
   against `/api/` before moving on. You get three tries, because a pasted
   token is easy to truncate.
3. **The MCP Server integration.** If `<url>/mcp_server/sse` does not answer,
   it tells you where to add it and offers to look again once you have.
4. **The mute entity**, if you want one.

Say no to the first question and the house is left out of the config, so
`parlour doctor` does not fail on something you do not have. Run `init` again
whenever you change your mind. Every answer
defaults to what is already set, and a token that still works is kept rather
than asked for twice. To set just the token, `parlour secrets set HA_TOKEN`
reads one from stdin.

## What Parlour can do with it

Tools come from three places, and the model sees them as one list:

- **Over MCP** (`mcp: true`). Home Assistant's **Model Context Protocol
  Server** integration publishes everything exposed under **Settings > Voice
  assistants > Expose** as tools, and Parlour inherits them all. Expose more
  and Parlour can do more, with nothing to change on the Parlour side. Add the
  integration under **Settings > Devices & services**. It has no options. If
  Parlour cannot see a light, expose the light.
- **Over REST** (`rest: true`). Two general tools cover what the MCP tools
  cannot: `ha_get_state` reads any entity, and `ha_call_service` calls any
  service. Its description tells the model to use it only when no more
  specific tool fits.

- **Through Assist** (`assist: true`, off by default). `ha_assist` passes a
  command, word for word, to
  [Assist](https://www.home-assistant.io/voice_control/), Home Assistant's own
  voice assistant, and returns what it said. See below.

Parlour connects to the MCP endpoint at `<url>/mcp_server/sse` with the token.
If that connection fails, the failure is logged and the REST tools still reach
the house.

## Assist

Assist is Home Assistant's built-in conversation agent. It is off by default,
because the MCP tools are the better way to control the house: the model sees
each entity and action and picks the right one, where Assist has to match the
words. Turn it on when you have set up Assist for things MCP cannot reach, and
it keeps working when Parlour is the one listening:

```json
{ "integrations": { "home-assistant": { "assist": true } } }
```

- **Custom sentences.** An automation with a sentence trigger, such as
  "good night" or "movie time", only fires when Assist hears the sentence.
  Parlour's own microphone, satellites and phones hear it instead, so the
  model passes it on through `ha_assist` and the automation runs.
- **Its languages.** Assist parses commands in over 50 languages. `language`
  picks one, such as `en` or `de`; empty uses the default set in Home
  Assistant under **Settings > Voice assistants**.
- **What it answers.** Whatever Assist says back, such as "Turned on the
  lights" or a sentence trigger's own response, becomes the tool's result. If
  Assist cannot handle the command, the model is told why and can try
  another tool.

Parlour always asks the built-in agent, `conversation.home_assistant`, not the
one your pipeline uses. So a house that also uses Parlour as its conversation
agent (below) cannot send a command round in a circle. Assist only sees
entities exposed under **Settings > Voice assistants > Expose**, the same list
the MCP Server integration publishes.

To make an announcement on a Home Assistant voice satellite, such as a Voice
PE, the model can call `assist_satellite.announce` with `ha_call_service`,
with a `message` in its data. That needs only the REST tools, not `assist`.

## Home Assistant as a client

It works the other way round too. Home Assistant, and every Voice PE
satellite through it, can use Parlour as its conversation agent. Home
Assistant keeps handling the wake word, speech to text and the spoken reply.
Only the thinking moves.

1. **Settings > Devices & services > Add integration > OpenAI Conversation**.
2. Base URL `http://<the server>:8765/v1`, and your `PARLOUR_TOKEN` as the
   API key.
3. Model `parlour`. The prompt and temperature on that page are ignored,
   because Parlour brings its own persona and tools.
4. **Settings > Voice assistants**, and set the pipeline's conversation agent
   to it.

Your satellites then use the local model for house control and hand harder
questions to the cloud, just as the server's own microphone does. Each Home
Assistant user gets a conversation of their own.

For an automation that wants a text answer, call `POST /ask` through a
`rest_command`, with the token kept in `secrets.yaml`:

```yaml
rest_command:
  ask_parlour:
    url: http://<the server>:8765/ask
    method: POST
    headers:
      authorization: !secret parlour_token
    content_type: application/json
    payload: '{"text": "{{ text }}", "room": "{{ room }}"}'
```

Here, `parlour_token` in `secrets.yaml` is `Bearer <the token>`. The reply is
`{"reply": "...", "via": "local"}`.

## Muting

`muteEntity` names an entity that makes Parlour ignore its wake word while it
is `on`. Parlour checks it over REST after the wake word fires and before it
acts on anything. So the house enforces the mute, not the machine running
Parlour, and it covers every wake word the server hears: its own microphone,
satellites and `/listen` devices in wake mode. Requests that arrive without
the server's wake word (the phone page, push mode, a satellite with
`localWake` on, `/ask` and `/v1`) are not muted. Empty, the default, means no
mute.

```json
{ "integrations": { "home-assistant": { "muteEntity": "input_boolean.parlour_muted" } } }
```

To follow a sleeping mode, define the boolean and an automation that tracks
it:

```yaml
input_boolean:
  parlour_muted:
    name: Parlour muted
    icon: mdi:microphone-off

automation:
  - id: parlour_mute_follows_sleeping
    alias: Parlour mute follows sleeping
    triggers:
      - trigger: state
        entity_id: input_boolean.sleeping
    actions:
      - action: "input_boolean.turn_{{ trigger.to_state.state }}"
        target:
          entity_id: input_boolean.parlour_muted
```

You can still override it by hand for one night from the dashboard. If Home
Assistant cannot be reached, the mute reads as off. Parlour should still hear
you, and the model will report the outage when you ask. A muted wake emits a
`muted` event on the `--events` stream, which is how the desktop app can tell
you why nothing happened.

## What the doctor checks

```text
ok    Home Assistant token  HA_TOKEN is set
ok    Home Assistant        http://homeassistant.local:8123
FAIL  Home Assistant MCP    http://homeassistant.local:8123/mcp_server/sse did not answer. Add the Model Context Protocol Server integration in Home Assistant.
```

A failure on the second line usually means the token. A failure on the third,
with the second fine, means the integration is not installed yet.
