Overview
Every OpenHome Ability falls into one of three categories, each with distinct triggers, lifecycles, and use cases.Skills
Triggered by user or brain routing. Runs once and exits.
Background Daemons
Runs automatically on session start. Loops continuously.
Local
Runs on-device hardware. Bypasses cloud sandbox.
Skills
Skills are the workhorse of OpenHome Abilities. A user says a hotword (or the brain’s routing LLM invokes it), the ability runs, does its thing, and hands control back viaresume_normal_flow().
When to Use Skills
- User-initiated actions (“play music”, “check weather”)
- API integrations (search web, fetch data)
- Interactive conversations (quizzes, advice)
- One-time tasks (send email, set alarm)
Lifecycle
Entry File
main.py — Contains your main logic
Example: Basic Advisor Skill
main.py
Key Pattern: Skills always end with
resume_normal_flow() to return control to the Agent.Common Skill Patterns
| Pattern | Description | Example |
|---|---|---|
| Fire-and-forget | Trigger → Execute → Exit | Send email, play sound |
| API Call | Fetch data → Speak result → Exit | Weather, web search |
| Interactive | Loop with listen → respond → exit command | Quiz game, advisor |
| LLM Translator | Voice → LLM → Command → Execute | Terminal control, smart home |
Background Daemons
Background Daemons start automatically when a user connects and run in awhile True loop for the entire session. No hotword needed. They can monitor conversations, poll APIs, watch for time-based events, and interrupt the main flow when something fires.
When to Use Background Daemons
- Time-based triggers (alarms, reminders, Pomodoro timer)
- Continuous monitoring (conversation logger, baby monitor)
- Periodic checks (stock prices, news feed)
- Ambient intelligence (context accumulation, user profiling)
Lifecycle
Entry File
watcher.py — Contains the daemon loop
Daemons can be standalone (
watcher.py only) or combined with a Skill (main.py + watcher.py).Example: Standalone Watcher Daemon
watcher.py
Example: Combined Skill + Daemon (Alarm)
The Alarm ability uses bothmain.py (to set alarms) and watcher.py (to fire them):
main.py — Parses user input and writes alarm to alarms.jsonwatcher.py — Polls alarms.json every 5 seconds and fires alarms when due
They coordinate through shared file storage, not direct function calls.
watcher.py (simplified)
Key Pattern: Daemons use
session_tasks.sleep() (not asyncio.sleep()) and never call resume_normal_flow().Critical Rules for Daemons
| Rule | Why |
|---|---|
Use session_tasks.sleep() | Ensures proper cleanup when session ends |
No resume_normal_flow() | Daemons don’t own the conversation |
Call send_interrupt_signal() before speaking | Prevents audio overlap |
| Delete then write for JSON files | write_file() appends — always delete first |
Daemon vs Skill: When to Choose
| Use a Skill if… | Use a Daemon if… |
|---|---|
| User explicitly triggers it | It should run automatically |
| One-time action | Continuous monitoring |
| Immediate response | Time-based or event-driven |
| No background state | Needs to watch conversations |
Local Abilities
Local Abilities run directly on Raspberry Pi hardware, bypassing the cloud sandbox entirely. They can use unrestricted Python packages, GPIO pins, and local models.Local Abilities are currently under active development. Full DevKit SDK coming soon.
When to Use Local
- Direct hardware control (GPIO, sensors, LEDs)
- Local-only processing (privacy-sensitive data)
- Unrestricted package requirements
- Low-latency operations
- Desktop terminal control
Entry File
main.py — Uses exec_local_command() to bridge cloud ↔ device
Example: Mac Terminal Control
main.py
Key Method:
exec_local_command() bridges the cloud sandbox to your local device via WebSocket.Sandbox Escape via OpenClaw
OpenHome Abilities run in a restricted cloud sandbox. To escape it, use OpenClaw — a desktop AI agent with 2,800+ community skills:Comparison Table
| Feature | Skill | Background Daemon | Local |
|---|---|---|---|
| Trigger | User hotword or brain routing | Automatic on session start | User or system |
| Lifecycle | Runs once, exits | Runs continuously in loop | Runs on-device |
| Entry File | main.py | watcher.py | main.py |
| Use Case | Interactive tasks | Monitoring, timers | Hardware, terminal |
Calls resume_normal_flow() | ✅ Yes | ❌ No | ✅ Yes |
| Runs in cloud sandbox | ✅ Yes | ✅ Yes | ❌ No (local device) |
| Can interrupt conversation | ✅ Yes | ✅ Yes (via send_interrupt_signal()) | ✅ Yes |
Next Steps
Trigger Words
Learn how to configure trigger words for your Skills
Templates
Explore starter templates for each ability type
SDK Reference
Full documentation of all available SDK methods
Build Your First Ability
Follow the 5-minute quickstart guide
