Install
openclaw skills install @vincsta/home-assistant-hubReal-time Home Assistant monitoring, alert rules, TTS voice notifications on Echo devices, Telegram delivery, entity inspection. Service calls are HARD-DENIED by default — require explicit safe-domains opt-in.
openclaw skills install @vincsta/home-assistant-hubThis skill can invoke Home Assistant services to change physical device states. Service calls are disabled by default.
The following domains are HARD-LOCKED in code and cannot be enabled:
lock.* — door locks (physical security)alarm_control_panel.* — alarm systems (safety-critical)cover.* — blinds/doors/garage (privacy/security)Service calls require domains to be listed in call_safe_domains in config/hub.json. An empty list means all service calls are blocked.
Default safe domains in the example config: light, climate, scene, media_player, automation, notify
To add a new domain, edit hub.json:
"call_safe_domains": ["light", "climate", "scene", "media_player", "automation", "notify"]
Before executing any service call, use --dry-run to preview what would happen:
node scripts/ha-cmd.js call climate.set_temperature entity_id=climate.hvac temperature=22 --dry-run
# Output: Service URL + payload WITHOUT executing
## 📡 External Data Transmission
This skill transmits data to third-party services:
- **Telegram Bot API** — alert messages including occupancy, sensor states, routines → exposes household patterns and security-relevant information
- **Echo devices via HA** — TTS announcements broadcast inside the home environment
- **Home Assistant instance** — all device telemetry sent over network
**Do NOT include sensitive personal data in alert templates.** Notification content is visible on Telegram accounts and potentially logged by Telegram servers.
### 🚫 No Hardcoded Credentials
All credentials are loaded exclusively from `config/hub.json`. There are no hardcoded fallback tokens anywhere in the codebase. If `telegram_bot_token` or `telegram_chat_id` are missing, the deliverer will refuse to send (with a warning) rather than fall back to defaults.
## 🔐 Credential Sensitivity
All secrets (`ha_token`, `telegram_bot_token`, `telegram_chat_id`) are stored in `config/hub.json`. This file is gitignored but:
- The HA token is a **long-lived bearer token** with broad API access — treat it like a password
- Default `ha_url` uses plain HTTP → credentials transmitted in cleartext on the local network. Use HTTPS if possible.
- Rotate tokens regularly via HA → Profile → Long-Lived Access Tokens
## 🔧 Process Management
The hub runs as background processes (`nohup`, `disown`) that persist beyond your session. `pkill -f "ha-hub.js start"` can match multiple processes. Always verify process state before killing.
---
# Home Assistant Hub
Real-time monitoring of Home Assistant device states with configurable alert rules, TTS voice notifications on Echo devices via Parla entities, Telegram delivery for alerts and events, entity inspection (states, history, persons, areas), and controlled device management through direct service calls.
## Architecture Overview
┌───────────┐ HTTP/WS ┌───────────────┐ JSON file ┌─────────────────┐ │ Home │ ◄──────────► │ ha-hub.js │ ───────────► │ telegram-deliver│ │ Assistant │ polling or │ background │ │ background │ │ │ WebSocket │ monitoring │ │ notification │ └───────────┘ └───────────────┘ │ delivery │ └────────┬────────┘ │ HTTPS ┌─────▼──────┐ │ Telegram API│ └────────────┘
┌───────────┐ HTTP ┌───────────────┐ │ ha-cmd.js │ ───────────────► │ Home Assistant│ (on-demand, direct) │ (CLI/API) │ │ WebSocket │ └───────────┘ └───────────────┘
**Two background processes:**
| Process | Role | Frequency |
|---------|------|-----------|
| `ha-hub.js` | Connected to HA, monitors device states against alert rules | Polls every 10s (WS fallback) |
| `telegram-deliver.js` | Reads pending notification files and sends via Telegram | Checks every 30s |
## Permissions
| Permission | Direction | Targets | Reason |
|-----------|-----------|---------|---------|
| `network` | outbound | Home Assistant API/WebSocket (HTTP+WSS), Telegram Bot API (HTTPS), Echo devices via HA notify | Real-time home monitoring, alert delivery to third-party servers, voice announcements |
| `network` | local-bind | localhost:9123 (on-demand HTTP API) — **disabled by default** | Local service for ha-cmd.js when enabled in config |
| `secrets` | local-read | `config/hub.json` | HA long-lived bearer token + Telegram credentials. File is gitignored — never commit |
## Quick Start
```bash
cd skills/home-assistant-hub
# Test connection to HA (safe, read-only)
node scripts/ha-cmd.js info
# List entities containing "echo" or "parla" (safe, read-only)
node scripts/ha-cmd.js state list 2>&1 | grep -i echo
# Start the monitoring hub (background process)
node scripts/ha-hub.js start
# Check hub status
node scripts/ha-hub.js status
# Stop the hub
node scripts/ha-hub.js stop
These commands only read data from Home Assistant. No device state changes:
| Command | What it does | Data exposed |
|---|---|---|
node scripts/ha-cmd.js info | HA version, URL, OS, connected clients | System metadata |
node scripts/ha-cmd.js state | All entity states at once | Full home telemetry snapshot |
node scripts/ha-cmd.js state get <id> | Single entity state (e.g., sensor.battery_level) | One sensor value |
node scripts/ha-cmd.js state list <domain> | Filter entities by domain (light, binary_sensor, etc.) | Domain-level inventory |
node scripts/ha-cmd.js scenes | List all defined scenes | Scene configuration |
node scripts/ha-cmd.js persons | List persons + presence states | Occupancy data — who is home |
node scripts/ha-cmd.js areas | Areas with device counts | Home layout and device inventory |
Sends voice announcements to Echo devices via HA Parla entities. This produces audible output in your physical environment.
Use comma-separated entity IDs — do NOT pass a JSON array:
# Send to ALL configured Echo devices simultaneously
node scripts/ha-cmd.js call notify.send_message \
entity_id="notify.echo_show_5_parla,notify.echo_pop_di_vincenzo_parla" \
message="Ciao Vincenzo, la batteria è al 75 percento"
# Single device
node scripts/ha-cmd.js call notify.send_message \
entity_id="notify.echo_show_5_parla" \
message="La cena è pronta"
Find Parla entity IDs: node scripts/ha-cmd.js state list 2>&1 | grep -i parla
notify.send_message, (3) Echo devices are online/unmuted.node scripts/ha-cmd.js state get notify.echo_show_5_parla | grep last_updatedConfigurable rules that detect device state changes and trigger notifications via Telegram or voice announcements.
node scripts/ha-hub.js add-rule
node scripts/ha-hub.js add-rules << 'EOF'
[
{
"name": "Garage aperto",
"entity_id": "binary_sensor.garage_door",
"condition": "state",
"value": "on",
"cooldown": 300,
"title": "Garage",
"template": "Il garage è aperto!"
},
{
"name": "Batteria bassa",
"entity_id": "sensor.battery_level",
"condition": "below",
"value": "20",
"cooldown": 600,
"title": "🪫 Batteria",
"template": "Batteria al {{state}}% — serve attenzione"
}
]
EOF
node scripts/ha-hub.js rules
| Condition | Meaning | Example value |
|---|---|---|
state | State equals value | on, home, open |
not_state | State not equals value | away |
above | Numeric state above threshold | 25 |
below | Numeric state below threshold | 10 |
changed | Always trigger on any change | — |
Alerts are delivered to a Telegram chat when configured. ⚠️ Data is transmitted externally to Telegram servers. Alert content includes entity states, occupancy information, and home status. Avoid including sensitive personal data in alert templates.
node scripts/telegram-deliver.js start # Start delivery process
node scripts/telegram-deliver.js status # Check delivery status
node scripts/telegram-deliver.js stop # Stop delivery process
Telegram settings in config/hub.json:
"telegram_bot_token": "your_bot_token",
"telegram_chat_id": "your_chat_id",
"notification_channel": "telegram" // or: "both" (Telegram + Echo)
ha-hub.js detects change against active alert rulesnotifications/ directorytelegram-deliver.js picks up the new file and sends to Telegramdelivered/ on successful deliveryha-cmd.js)All commands run from the skill directory:
cd skills/home-assistant-hub
| Command | Description |
|---|---|
node scripts/ha-cmd.js info | HA version, URL, OS |
node scripts/ha-cmd.js state | All entity states |
node scripts/ha-cmd.js state get <id> | Specific entity (e.g., sensor.battery) |
node scripts/ha-cmd.js state list <domain> | Filter by domain (light, binary_sensor...) |
node scripts/ha-cmd.js scenes | List all defined scenes |
node scripts/ha-cmd.js persons | List persons + presence states |
node scripts/ha-cmd.js areas | Areas with device counts |
Service calls require explicit domain opt-in in config/hub.json via call_safe_domains. No service is callable unless its domain is listed there. Dangerous domains (lock, alarm_control_panel, cover) are hard-locked and cannot be enabled.
| Command | Description |
|---|---|
node scripts/ha-cmd.js call <service> [key=value ...] --dry-run | Preview what would be called (no execution) |
node scripts/ha-cmd.js call <service> [key=value ...] | Execute service — only if domain is in call_safe_domains |
# TTS announcement on Echo devices
node scripts/ha-cmd.js call notify.send_message \
entity_id="notify.echo_show_5_parla" message="Prova vocale"
# Turn on a light — changes physical environment
node scripts/ha-cmd.js call light.turn_on entity_id=light.living_room
# Set thermostat temperature — affects comfort
node scripts/ha-cmd.js call climate.set_temperature \
entity_id=climate.hvac temperature=22
# Activate a scene (multi-device action)
node scripts/ha-cmd.js call scene.turn_on entity_id=scene.movie_time
# Trigger an automation
node scripts/ha-cmd.js call automation.trigger entity_id=automation.my_automation
config/hub.json)Use hub.example.json as a template:
cp config/hub.example.json config/hub.json
# Edit hub.json with your credentials — NEVER commit this file!
{
"ha_url": "http://homeassistant.local:8123", // use https:// if available
"ha_token": "your-long-lived-token", // treat as password
"poll_interval": 10, // seconds between polls
"rules": [...], // alert rules (see above)
"notification_channel": "telegram", // or: "both"
"quiet_hours": { // suppress alerts during sleep
"enabled": true,
"start": "22:30",
"end": "08:30"
},
"on_demand": { // local API port for ha-cmd.js (disabled by default)
"enabled": false,
"port": 9123
},
"call_safe_domains": ["light", "climate", "scene", "media_player", "automation", "notify"]
"telegram_bot_token": "...",
"telegram_chat_id": "...",
"echo_devices": {
"all_devices_announce_id": "", // legacy: Alexa announce group (unused)
"echo_pop_device_id": "notify.echo_pop_di_vincenzo_parla",
"echo_show_device_id": "notify.echo_show_5_parla"
}
}
Suppress alerts during sleep in config/hub.json:
"quiet_hours": {
"enabled": true,
"start": "22:00",
"end": "08:00"
}
Rules during quiet hours are silently suppressed. To override (e.g., urgent alert), call notify.send_message directly — it bypasses rules entirely.
skills/home-assistant-hub/
├── SKILL.md ← this file (read first!)
├── config/
│ ├── hub.json ← runtime config (secrets, gitignored)
│ └── hub.example.json ← template with safe defaults
├── scripts/
│ ├── ha-hub.js ← WebSocket + polling monitoring engine
│ ├── telegram-deliver.js ← Telegram notification delivery daemon
│ └── ha-cmd.js ← on-demand read-only + service call CLI (with allowlist enforcement)
├── notifications/ ← pending notification JSON files (gitignored)
├── delivered/ ← successfully delivered notifications (gitignored)
├── logs/ ← daily rotation logs (gitignored)
├── references/
│ └── setup.md ← detailed setup guide
├── start.sh ← portable quick-start script
├── daemon.sh ← persistent daemon wrapper
└── .gitignore
| Problem | Solution |
|---|---|
| HA connection fails | Check token in HA → Profile → Long-Lived Access Tokens |
| No alerts firing | Verify entity IDs with ha-cmd.js state list + grep |
| WS fails but polling works | Normal — hub auto-fallbacks to polling mode |
| Duplicate alerts | Increase cooldown in rule config (seconds) |
| TTS no audio | Use comma-separated Parla entities. Verify entities exist first with state list. |
| Telegram not delivering | Check bot_token and chat_id in hub.json; restart telegram-deliver |
| "Service domain blocked" | Add the domain to call_safe_domains in hub.json (see Security section above) |