website/docs/user-guide/messaging/homeassistant.md
Hermes Agent integrates with Home Assistant in two ways:
# Add to ~/.hermes/.env
# Required: your Long-Lived Access Token
HASS_TOKEN=your-long-lived-access-token
# Optional: HA URL (default: http://homeassistant.local:8123)
HASS_URL=http://192.168.1.100:8123
:::info
The homeassistant toolset is automatically enabled when HASS_TOKEN is set. Both the gateway platform and the device control tools activate from this single token.
:::
hermes gateway
Home Assistant will appear as a connected platform alongside any other messaging platforms (Telegram, Discord, etc.).
Hermes Agent registers four tools for smart home control:
ha_list_entitiesList Home Assistant entities, optionally filtered by domain or area.
Parameters:
domain (optional) — Filter by entity domain: light, switch, climate, sensor, binary_sensor, cover, fan, media_player, etc.area (optional) — Filter by area/room name (matches against friendly names): living room, kitchen, bedroom, etc.Example:
List all lights in the living room
Returns entity IDs, states, and friendly names.
ha_get_stateGet detailed state of a single entity, including all attributes (brightness, color, temperature setpoint, sensor readings, etc.).
Parameters:
entity_id (required) — The entity to query, e.g., light.living_room, climate.thermostat, sensor.temperatureExample:
What's the current state of climate.thermostat?
Returns: state, all attributes, last changed/updated timestamps.
ha_list_servicesList available services (actions) for device control. Shows what actions can be performed on each device type and what parameters they accept.
Parameters:
domain (optional) — Filter by domain, e.g., light, climate, switchExample:
What services are available for climate devices?
ha_call_serviceCall a Home Assistant service to control a device.
Parameters:
domain (required) — Service domain: light, switch, climate, cover, media_player, fan, scene, scriptservice (required) — Service name: turn_on, turn_off, toggle, set_temperature, set_hvac_mode, open_cover, close_cover, set_volume_levelentity_id (optional) — Target entity, e.g., light.living_roomdata (optional) — Additional parameters as a JSON objectExamples:
Turn on the living room lights
→ ha_call_service(domain="light", service="turn_on", entity_id="light.living_room")
Set the thermostat to 22 degrees in heat mode
→ ha_call_service(domain="climate", service="set_temperature",
entity_id="climate.thermostat", data={"temperature": 22, "hvac_mode": "heat"})
Set living room lights to blue at 50% brightness
→ ha_call_service(domain="light", service="turn_on",
entity_id="light.living_room", data={"brightness": 128, "color_name": "blue"})
The Home Assistant gateway adapter connects via WebSocket and subscribes to state_changed events. When a device state changes and matches your filters, it's forwarded to the agent as a message.
:::warning Required Configuration
By default, no events are forwarded. You must configure at least one of watch_domains, watch_entities, or watch_all to receive events. Without filters, a warning is logged at startup and all state changes are silently dropped.
:::
Configure which events the agent sees in ~/.hermes/config.yaml under the Home Assistant platform's extra section:
platforms:
homeassistant:
enabled: true
extra:
watch_domains:
- climate
- binary_sensor
- alarm_control_panel
- light
watch_entities:
- sensor.front_door_battery
ignore_entities:
- sensor.uptime
- sensor.cpu_usage
- sensor.memory_usage
cooldown_seconds: 30
| Setting | Default | Description |
|---|---|---|
watch_domains | (none) | Only watch these entity domains (e.g., climate, light, binary_sensor) |
watch_entities | (none) | Only watch these specific entity IDs |
watch_all | false | Set to true to receive all state changes (not recommended for most setups) |
ignore_entities | (none) | Always ignore these entities (applied before domain/entity filters) |
cooldown_seconds | 30 | Minimum seconds between events for the same entity |
:::tip
Start with a focused set of domains — climate, binary_sensor, and alarm_control_panel cover the most useful automations. Add more as needed. Use ignore_entities to suppress noisy sensors like CPU temperature or uptime counters.
:::
State changes are formatted as human-readable messages based on domain:
| Domain | Format |
|---|---|
climate | "HVAC mode changed from 'off' to 'heat' (current: 21, target: 23)" |
sensor | "changed from 21°C to 22°C" |
binary_sensor | "triggered" / "cleared" |
light, switch, fan | "turned on" / "turned off" |
alarm_control_panel | "alarm state changed from 'armed_away' to 'triggered'" |
| (other) | "changed from 'old' to 'new'" |
Outbound messages from the agent are delivered as Home Assistant persistent notifications (via persistent_notification.create). These appear in the HA notification panel with the title "Hermes Agent".
HASS_TOKEN authenticates the connection)The Home Assistant tools enforce security restrictions:
:::warning Blocked Domains The following service domains are blocked to prevent arbitrary code execution on the HA host:
shell_command — arbitrary shell commandscommand_line — sensors/switches that execute commandspython_script — scripted Python executionpyscript — broader scripting integrationhassio — addon control, host shutdown/rebootrest_command — HTTP requests from HA server (SSRF vector)Attempting to call services in these domains returns an error. :::
Entity IDs are validated against the pattern ^[a-z_][a-z0-9_]*\.[a-z0-9_]+$ to prevent injection attacks.
User: Start my morning routine
Agent:
1. ha_call_service(domain="light", service="turn_on",
entity_id="light.bedroom", data={"brightness": 128})
2. ha_call_service(domain="climate", service="set_temperature",
entity_id="climate.thermostat", data={"temperature": 22})
3. ha_call_service(domain="media_player", service="turn_on",
entity_id="media_player.kitchen_speaker")
User: Is the house secure?
Agent:
1. ha_list_entities(domain="binary_sensor")
→ checks door/window sensors
2. ha_get_state(entity_id="alarm_control_panel.home")
→ checks alarm status
3. ha_list_entities(domain="lock")
→ checks lock states
4. Reports: "All doors closed, alarm is armed_away, all locks engaged."
When connected as a gateway platform, the agent can react to events:
[Home Assistant] Front Door: triggered (was cleared)
Agent automatically:
1. ha_get_state(entity_id="binary_sensor.front_door")
2. ha_call_service(domain="light", service="turn_on",
entity_id="light.hallway")
3. Sends notification: "Front door opened. Hallway lights turned on."
Environment variables not picked up.
The adapter reads credentials from ~/.hermes/.env (auto-merged at startup) or
from config.yaml. Double-check the file lives under the active Hermes profile
home and that there's no stray quoting around the URL/token. Restart the gateway
after editing — env changes are only applied on process start.
conversation entity not found / agent never replies.
Home Assistant's conversation API requires a configured Assist conversation
agent. In HA, open Settings → Voice assistants → Add assistant and note the
resulting entity id (looks like conversation.home_assistant or
conversation.openai_<name>). Set that entity id in the adapter's
conversation_entity setting; the default may not exist on your instance.
REST auth failing (401 Unauthorized).
The token must be a Long-Lived Access Token created from your HA user profile
page (Profile → Security → Long-lived access tokens). Short-lived UI
session tokens won't work. Also verify the base URL includes the scheme and
port (e.g. http://homeassistant.local:8123) and is reachable from the host
running Hermes — curl -H "Authorization: Bearer <token>" <url>/api/ should
return {"message": "API running."}.