Documentation Index Fetch the complete documentation index at: https://docs.openhome.com/llms.txt
Use this file to discover all available pages before exploring further.
OpenClaw lets your OpenHome agent control your local machine through voice — launch apps, monitor system status, manage files, run developer workflows, and more. The Ability sends a command through OpenHome’s exec_local_command() call; the OpenClaw client executes it on your machine; the result comes back as a spoken response.
For a short task-oriented quickstart, see Getting Started → OpenClaw . This page is the full reference.
What you can build
Application launcher and manager
System monitoring and diagnostics
File and folder automation
Development environment controller
Custom workflow automations
Smart home integration via computer
Screenshot and screen recording tools
Clipboard and text manipulation
Setup
1. Install OpenClaw
OpenClaw must be installed and configured on your local machine with an LLM API key.
npm install -g openclaw@latest
openclaw onboard --install-daemon
During onboarding you’ll be prompted for an LLM API key (OpenAI, Anthropic, etc.). OpenClaw uses this key to interpret natural-language commands.
2. Download the OpenClaw client
Download for your OS :
Windows — .exe installer
macOS — .dmg or .app
Linux — AppImage or .deb
3. Run the client
Launch
Windows — run the .exe, allow permissions if prompted
macOS — if blocked, go to System Settings → Privacy & Security → Open Anyway
Linux — chmod +x and run, grant required permissions
Get your OpenHome API key
Connect
Paste the key into the OpenClaw client, click Connect . Wait for the “welcome” message in the logs — that confirms a live connection.
4. Add the OpenClaw Ability
Install the OpenClaw Ability from the Abilities library on your Agent. This is the template you’ll customize for your use case.
The exec_local_command() API
One function carries every command from the Ability to the OpenClaw client.
async def exec_local_command (
self ,
command : str | dict ,
target_id : str | None = None ,
timeout : float = 10.0 ,
)
Parameters:
command (str | dict, required) — inquiry or command for OpenClaw
target_id (str | None) — target device identifier (default: "laptop")
timeout (float) — max seconds to wait for a response (default: 10.0)
Returns: str — response from OpenClaw (success message, error, or command output).
Usage
# Basic
response = await self .capability_worker.exec_local_command(user_inquiry)
# Long-running command — longer timeout
response = await self .capability_worker.exec_local_command(
"compile large project" ,
timeout = 30.0 ,
)
# Specific target device
response = await self .capability_worker.exec_local_command(
"check battery status" ,
target_id = "laptop" ,
)
How it works
User speaks a computer-control command
OpenHome captures voice as text
Ability sends the command to your local OpenClaw client via exec_local_command()
OpenClaw executes on your computer
OpenClaw returns the result (success / failure / output)
An LLM converts the technical output into a natural spoken response (max ~15 words)
OpenHome speaks the result
Example abilities
1. Development environment controller
# Trigger: "start coding session"
# Opens IDE, starts local servers, opens documentation
async def first_function ( self ):
commands = [
"open Visual Studio Code" ,
"start local dev server on port 3000" ,
"open browser to localhost:3000" ,
]
for cmd in commands:
await self .capability_worker.exec_local_command(cmd)
await self .capability_worker.speak( "Development environment is ready." )
self .capability_worker.resume_normal_flow()
2. System health monitor
# Trigger: "check system health"
async def first_function ( self ):
metrics = [
( "CPU usage" , "get cpu usage" ),
( "Memory usage" , "get memory usage" ),
( "Disk space" , "get disk usage" ),
( "Battery level" , "get battery level" ),
]
report = []
for name, cmd in metrics:
response = await self .capability_worker.exec_local_command(cmd)
report.append( f " { name } : { response } " )
await self .capability_worker.speak( ", " .join(report))
self .capability_worker.resume_normal_flow()
3. Smart screenshot
# Trigger: "take screenshot of active window"
async def first_function ( self ):
user_inquiry = await self .capability_worker.wait_for_complete_transcription()
if "full screen" in user_inquiry.lower():
cmd = "screenshot fullscreen save to ~/Desktop"
elif "active window" in user_inquiry.lower():
cmd = "screenshot active window save to ~/Desktop"
else :
cmd = "screenshot selection save to ~/Desktop"
response = await self .capability_worker.exec_local_command(cmd, timeout = 15.0 )
await self .capability_worker.speak( f "Screenshot saved: { response } " )
self .capability_worker.resume_normal_flow()
4. App manager with confirmation
# Trigger: "close all browsers"
async def first_function ( self ):
response = await self .capability_worker.exec_local_command( "list open browsers" )
if "none" in response.lower():
await self .capability_worker.speak( "No browsers are open." )
self .capability_worker.resume_normal_flow()
return
await self .capability_worker.speak( f "Found: { response } . Close all?" )
confirmation = await self .capability_worker.user_response()
if "yes" in confirmation.lower():
await self .capability_worker.exec_local_command( "close all browsers" )
await self .capability_worker.speak( "All browsers closed." )
else :
await self .capability_worker.speak( "Cancelled." )
self .capability_worker.resume_normal_flow()
Best practices
1. Define clear trigger words
Specific, unambiguous triggers beat generic ones:
✅ start development session, launch dev environment, open my coding setup
❌ start, go, do it
Avoid trigger phrases that collide with other Abilities.
2. Validate before executing
DANGEROUS_COMMANDS = [ "rm -rf" , "format" , "delete system" , "shutdown -h now" ]
if any (danger in user_inquiry.lower() for danger in DANGEROUS_COMMANDS ):
await self .capability_worker.speak( "I can't execute that for safety reasons." )
return
3. Confirm destructive actions
if "restart" in user_inquiry.lower() or "shutdown" in user_inquiry.lower():
confirmed = await self .capability_worker.run_confirmation_loop(
"This will restart your computer. Are you sure?"
)
if not confirmed:
await self .capability_worker.speak( "Cancelled." )
return
4. Tune timeouts
# Default is fine for quick commands
response = await self .capability_worker.exec_local_command( "open Chrome" )
# Long-running — bump the timeout
response = await self .capability_worker.exec_local_command(
"compile entire project" ,
timeout = 60.0 ,
)
Don’t just echo raw OpenClaw output. Parse and shape:
response = await self .capability_worker.exec_local_command( "get battery level" )
# Raw: "Battery: 73% (charging, 2:15 remaining)"
# Spoken: "Battery is at 73 percent."
battery = extract_percentage(response)
await self .capability_worker.speak( f "Battery is at { battery } percent." )
Keep spoken output to 1 sentence, 15 words or less .
6. Handle errors and timeouts
try :
response = await self .capability_worker.exec_local_command(user_inquiry, timeout = 15.0 )
if "error" in response.lower() or "failed" in response.lower():
await self .capability_worker.speak( "That command didn't work. Try something else." )
else :
# process success
...
except asyncio.TimeoutError:
await self .capability_worker.speak( "That took too long. It might still be running." )
except Exception as e:
self .worker.editor_logging_handler.error( f "Command failed: { e } " )
await self .capability_worker.speak( "Something went wrong. Check the logs." )
7. Chain commands for workflows
workflow = [
( "Opening calendar" , "open Calendar app" ),
( "Starting video" , "open Zoom" ),
( "Opening notes" , "open Notes app" ),
]
for description, command in workflow:
await self .capability_worker.speak(description)
await self .capability_worker.exec_local_command(command)
await asyncio.sleep( 1 )
await self .capability_worker.speak( "Ready for your meeting." )
Troubleshooting
OpenClaw client won't connect
Verify your API key is correct (from Dashboard → Settings → API Keys)
Check the daemon is running: openclaw status
Restart the OpenClaw client app
Check the client logs for error messages
Commands time out or fail
Increase timeout: exec_local_command(command, timeout=20.0)
Check daemon status: openclaw status
Verify the command is valid for your OS
Review OpenClaw client logs
Permission errors on macOS
System Settings → Privacy & Security → find the blocked app → Open Anyway
Grant Accessibility and Automation permissions when prompted
Confirm the client shows “Connected”
Test a safe command first: “what time is it”
Verify the Ability is registered on your Agent
Review OpenClaw client logs
Security & privacy
OpenClaw runs with your user permissions on the local machine. Commands execute exactly as if you typed them in a terminal.
Commands run locally — not sent to external servers (the LLM used by OpenClaw may receive the natural-language text for command generation)
Your OpenHome API key authenticates the OpenHome → OpenClaw connection
Always add validation for user-provided input
Use confirmation prompts for destructive operations (restart, delete, format)
Review all permissions carefully when installing the client
Architecture
Voice Input → OpenHome Ability → exec_local_command()
↓
OpenClaw Client
(via WebSocket)
↓
OpenClaw Daemon
(with LLM API)
↓
Local System Execution
(apps, files, etc.)
↓
Response ← AI Formatting ← Template
Resources