Parlour

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:

{
  "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, 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:

{ "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:

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.

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

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

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

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.

On this page