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:
- The address. It tries the one in your config, then
homeassistant.local:8123,homeassistant:8123andlocalhost:8123. It takes the first that answers the way Home Assistant does:401from/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. - 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. - The MCP Server integration. If
<url>/mcp_server/ssedoes not answer, it tells you where to add it and offers to look again once you have. - 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_statereads any entity, andha_call_servicecalls 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_assistpasses 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_assistand the automation runs. - Its languages. Assist parses commands in over 50 languages.
languagepicks one, such asenorde; 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.
- Settings > Devices & services > Add integration > OpenAI Conversation.
- Base URL
http://<the server>:8765/v1, and yourPARLOUR_TOKENas the API key. - Model
parlour. The prompt and temperature on that page are ignored, because Parlour brings its own persona and tools. - 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_mutedYou 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.