11 KiB
name, description, version, author, platforms, created_by, metadata
| name | description | version | author | platforms | created_by | metadata | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| homeassistant-integration | Connect Home Assistant to Hermes — token setup, toolset enablement, and smart home control via Hermes tools. | 1.2.0 | ray |
|
agent |
|
Home Assistant + Hermes Integration
Connect a Home Assistant instance to Hermes so the agent can list entities, read sensor states, discover services, and control devices (lights, switches, climate, media players, etc.) via the homeassistant toolset.
Prerequisites
- A running Home Assistant instance (local network or remote)
- A Long-Lived Access Token from your HA profile page (click your username → Security → Long-Lived Access Tokens)
- The HA server accessible from the Hermes host
Setup
1. Get a Long-Lived Access Token
In Home Assistant: click your user profile (bottom-left) → Security → Long-Lived Access Tokens → Create Token.
The token is a JWT that looks like:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...<rest of token>
You can decode it to verify:
python3 -c "
import base64, json
payload = base64.urlsafe_b64decode('PAYLOAD_SECTION' + '==')
data = json.loads(payload)
print(f'Issuer: {data[\"iss\"]}')
print(f'Expires: {data[\"exp\"]}')
"
HA long-lived access tokens typically expire in 10 years.
2. Set the Environment Variables
hermes config set HASS_URL http://YOUR_HA_HOST:8123
hermes config set HASS_TOKEN <your-long-lived-access-token>
hermes config set auto-detects secrets — the URL goes to config.yaml, the token goes to .env. The tool code reads both from environment variables, so if HASS_URL landed in config.yaml instead of .env, add it to .env as well:
echo 'HASS_URL=http://YOUR_HA_HOST:8123' >> ~/.hermes/.env
Verify they're in place:
grep HASS ~/.hermes/.env
3. Enable the Toolset
hermes tools enable homeassistant
Verify:
hermes tools list | grep homeassistant
# ✓ enabled homeassistant 🏠 Home Assistant
4. Verify the Token Works
Test with a direct API call before restarting the gateway:
python3 -c "
import json, urllib.request, subprocess
token = subprocess.run(['grep', 'HASS_TOKEN', '$HOME/.hermes/.env'],
capture_output=True, text=True).stdout.strip().split('=',1)[1]
req = urllib.request.Request('http://YOUR_HA_HOST:8123/api/',
headers={'Authorization': f'Bearer {token}'})
data = json.loads(urllib.request.urlopen(req, timeout=10).read())
print(f'HA: {data.get(\"location_name\", \"unnamed\")}')
"
Also check entity count:
# Same pattern, hit /api/states instead and count by domain
If you get HTTP 200, the token is valid and the URL is reachable.
5. Restart the Gateway
Tool changes require a new session. Restart the gateway:
hermes gateway restart
After restart, the agent has access to four HA tools:
| Tool | Purpose |
|---|---|
ha_list_entities |
List/filter entities by domain or area |
ha_get_state |
Get detailed state + attributes of one entity |
ha_list_services |
Discover available services (actions) per domain |
ha_call_service |
Control a device (turn_on, turn_off, set_temperature, etc.) |
Security
The homeassistant_tool.py implementation blocks these high-risk service domains for safety:
shell_command, command_line, python_script, pyscript, hassio, rest_command
Automation Patterns
Cron-Based Condition Monitoring
Use Hermes cron jobs to poll an HA entity and act when a condition is met (e.g., turn off AC when temp drops to target). The cron job should self-terminate after acting.
Recipe — trigger action on sensor threshold:
cronjob(action='create',
name='<descriptive name>',
prompt='Check <entity_id> using ha_get_state. If <condition>, call ha_call_service to <action> and report success, then use cronjob to remove this monitoring job (job_id in context). If condition not met, report current state briefly and do nothing.',
schedule='*/10 22-23,0-2 * * *', # every 10 min from 10pm–2am (use CRON EXPRESSION, not "10m" — that's a one-shot)
repeat=20,
toolsets=['homeassistant', 'cronjob'])
Key details:
- toolsets: Must include both
homeassistantandcronjobso the cron agent can check sensors, call services, and remove itself. - Schedule: Use
*/N hour-range * * *for time-windowed polling (e.g.,*/7 22-23,0-2 * * *= every 7 minutes from 10 PM through 2 AM). Avoid starting monitoring before the user wants it — use the cron expression to delay first run. - CRITICAL — schedule format: Simple time strings like
"10m"or"30m"are parsed as one-shot ("once in 10 minutes"), NOT recurring. The cron will run exactly once and then stop. Always use cron expressions (*/10 * * * *) or"every 10m"for recurring jobs. A job that silently runs once and completes without acting is the #1 failure mode for HVAC monitoring. - Repeat limit: Cap with
repeatto prevent indefinite polling. With N-minute intervals,repeat=20covers ~N×20 minutes. - Prompt must include self-removal: The job should call
cronjob(action='remove', job_id=...)after acting so it doesn't keep polling indefinitely. - Schedule corrections: Users often tweak poll intervals or start times after creation. Use
cronjob(action='update', job_id=..., schedule=...)rather than deleting and recreating.
Pitfalls
- Home Assistant behind VPS reverse proxy: When proxying HA through an external nginx (VPS + Tailscale), add the proxy's Tailscale IP to
trusted_proxiesinconfiguration.yamlor HA returns 400:Restart:http: use_x_forwarded_for: true trusted_proxies: - 127.0.0.1 - 100.86.68.23 # VPS Tailscale IPdocker restart homeassistant. Seedocker-service-deploymentskill,references/vps-migration.mdfor the full VPS setup pattern. - CRITICAL — Cron schedule format: Bare duration strings like
"10m"or"30m"are ONE-SHOT ("once in X minutes"). The job runs exactly once and silently completes — the #1 failure mode for HVAC monitoring. Always use cron expressions:"*/10 * * * *"(every 10 minutes),"*/7 22-23,0-2 * * *"(time-windowed). Alternative:"every 10m"shorthand. Verify withcronjob(action='list')after creation —"once in Nm"means it won't repeat. - Climate vs sensor entities: Climate entities (
climate.upstairs) are the thermostat — callha_call_serviceto control them. Sensor entities (sensor.upstairs_temperature) are read-only readings. For threshold monitoring, check the sensor for the reading but control the climate entity. - HASS_URL must be in .env: The tool reads
os.getenv("HASS_URL"), notconfig.yaml. Add it to.envexplicitly. - Docs 404: The Hermes docs page for Home Assistant integration returns 404. Canonical reference:
tools/homeassistant_tool.pyandgateway/config.py. - Subprocesses don't inherit .env: When testing with
python3 -c, read secrets viasubprocess.run(['grep', ...])instead ofos.getenv(). - Gateway restart required: Toolset changes need
hermes gateway restart. - Internal vs proxy URL: The tool needs the direct API URL (e.g.,
http://192.168.50.98:8123), not the external proxy port. - Entity registry may 404: Use
/api/config/config_entries/entryinstead to discover integrations and device metadata.
Two-Phase Monitoring (Wide → Narrow Polling)
When the target is far away, poll infrequently to save resources. When it nears the threshold, switch to tight polling automatically.
How it works: The cron job starts with a wide interval (e.g., every 30 min). When the sensor value enters a "close" zone, the job updates its own schedule to a tight interval (e.g., every 7 min). When the final threshold is hit, it acts and removes itself.
Recipe:
# Phase 1 — wide polling (every 30 min on the half-hour, 10pm–2am)
cronjob(action='create',
name='Turn off AC when temp hits 78°F',
prompt='Check climate.upstairs with ha_get_state. '
'If temp > 79°F: report briefly, do nothing (stay in wide-poll mode). '
'If temp ≤ 79°F and > 78°F: UPGRADE this job to tight polling — '
'cronjob(action=update, job_id=THIS_JOB_ID, schedule="*/7 22-23,0-2 * * *"). '\
'Report "now monitoring closely." '
'If temp ≤ 78°F: ha_call_service(domain=climate, service=turn_off, '
'entity_id=climate.upstairs), report success, remove this job.',
schedule='0,30 22-23,0-2 * * *',
repeat=20,
toolsets=['homeassistant','cronjob'])
Once upgraded to Phase 2, the job's prompt should know it's in tight mode: check temp, act if ≤ target, otherwise report and wait. The prompt must handle both modes so it works correctly after a self-upgrade.
Key points:
- Phase 1 prompt must include the self-upgrade logic (wide → tight) AND the final action logic (act + self-remove).
- Phase 2 prompt (after upgrade) only needs: check, act-if-condition-met, otherwise report.
- Use
repeatto cap total runs so the job doesn't poll forever if the condition never triggers. - Users often specify multiple constraints (interval, start time, threshold-based frequency). Ask clarifying questions if the intent is ambiguous, and prefer the two-phase pattern when the target is expected to drift slowly.
IoT Device Onboarding
For finding, identifying, and adding new WiFi IoT devices to the network and Home Assistant — WiFi setup, IP discovery, MAC vendor lookup, LAN mode enablement, and HA integration — see references/iot-device-onboarding.md.
Identifying Devices & Integrations
Thermostat Model / Integration Discovery
The entity states API (/api/states) doesn't expose manufacturer or model info. To find what integration provides a climate entity and what hardware it's connected to, query the HA config entries API:
import json, urllib.request
# List all config entries (integrations)
req = urllib.request.Request(
'http://HA_HOST:8123/api/config/config_entries/entry',
headers={'Authorization': f'Bearer {token}'})
entries = json.loads(urllib.request.urlopen(req).read())
for entry in entries:
if entry.get('domain') == 'honeywell': # or 'ecobee', 'nest', etc.
print(f"Title: {entry['title']}, State: {entry['state']}")
The domain field reveals the integration (e.g., honeywell, ecobee, nest). The entity registry endpoints (/api/config/entity_registry/*) may 404 depending on HA version or reverse-proxy setup — the config entries API is a reliable fallback.