Skip to main content

Home Assistant Integration

Hermes Agent integrates with Home Assistant in two ways:

  1. Gateway platform — subscribes to real-time state changes via WebSocket and responds to events
  2. Smart home tools — four LLM-callable tools for querying and controlling devices via the REST API

Setup

1. Create a Long-Lived Access Token

  1. Open your Home Assistant instance
  2. Go to your Profile (click your name in the sidebar)
  3. Scroll to Long-Lived Access Tokens
  4. Click Create Token, give it a name like "Hermes Agent"
  5. Copy the token

2. Configure Environment Variables

# 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.

3. Start the Gateway

hermes gateway

Home Assistant will appear as a connected platform alongside any other messaging platforms (Telegram, Discord, etc.).

Available Tools

Hermes Agent registers four tools for smart home control:

ha_list_entities

List 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_state

Get 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.temperature

Example:

What's the current state of climate.thermostat?

Returns: state, all attributes, last changed/updated timestamps.

ha_list_services

List 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, switch

Example:

What services are available for climate devices?

ha_call_service

Call a Home Assistant service to control a device.

Parameters:

  • domain (required) — Service domain: light, switch, climate, cover, media_player, fan, scene, script
  • service (required) — Service name: turn_on, turn_off, toggle, set_temperature, set_hvac_mode, open_cover, close_cover, set_volume_level
  • entity_id (optional) — Target entity, e.g., light.living_room
  • data (optional) — Additional parameters as a JSON object

Examples:

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"})

Gateway Platform: Real-Time Events

The Home Assistant gateway adapter connects via WebSocket and subscribes to state_changed events. When a device state changes, it's forwarded to the agent as a message.

Event Filtering

Configure which events the agent sees via platform config in the gateway:

# In platform extra config
{
"watch_domains": ["climate", "binary_sensor", "alarm_control_panel"],
"watch_entities": ["sensor.front_door"],
"ignore_entities": ["sensor.uptime", "sensor.cpu_usage"],
"cooldown_seconds": 30
}
SettingDefaultDescription
watch_domains(all)Only watch these entity domains
watch_entities(all)Only watch these specific entities
ignore_entities(none)Always ignore these entities
cooldown_seconds30Minimum seconds between events for the same entity
tip

Without any filters, the agent receives all state changes, which can be noisy. For practical use, set watch_domains to the domains you care about (e.g., climate, binary_sensor, alarm_control_panel).

Event Formatting

State changes are formatted as human-readable messages based on domain:

DomainFormat
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'"

Agent Responses

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".

Connection Management

  • WebSocket with 30-second heartbeat for real-time events
  • Automatic reconnection with backoff: 5s → 10s → 30s → 60s
  • REST API for outbound notifications (separate session to avoid WebSocket conflicts)
  • Authorization — HA events are always authorized (no user allowlist needed, since the HASS_TOKEN authenticates the connection)

Security

The Home Assistant tools enforce security restrictions:

Blocked Domains

The following service domains are blocked to prevent arbitrary code execution on the HA host:

  • shell_command — arbitrary shell commands
  • command_line — sensors/switches that execute commands
  • python_script — scripted Python execution
  • pyscript — broader scripting integration
  • hassio — addon control, host shutdown/reboot
  • rest_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.

Example Automations

Morning Routine

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")

Security Check

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."

Reactive Automation (via Gateway Events)

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."