The watcher template demonstrates standalone background daemons that start automatically when a user connects and run for the entire session.
What is it?
The Watcher template shows how to build background daemons that run independently of normal conversation flow. It demonstrates:- Running in a
while Trueloop that starts automatically on session start - Using
session_tasks.sleep()instead ofasyncio.sleep()for proper cleanup - Reading conversation history with
get_full_message_history() - Logging events without interrupting the main conversation
- No
resume_normal_flow()— daemons don’t own the conversation
When to use it
Use the watcher template when you’re building:- Conversation monitors — Log interactions, build user profiles, extract insights
- Schedulers — Poll for time-based events, fire reminders
- Alert systems — Watch for keywords, sentiment changes, or context shifts
- Background processors — Analyze ambient audio, accumulate data, build RAG indexes
Complete code
Key patterns
Background daemon lifecycle
Daemon starts automatically
Background daemons start when the user connects to an OpenHome agent — no trigger word needed.
Critical daemon rules
| Rule | Why |
|---|---|
Use session_tasks.sleep(), not asyncio.sleep() | Ensures proper cleanup when the session ends |
No resume_normal_flow() in the loop | Daemons are independent threads — they don’t own the conversation |
Call send_interrupt_signal() before speaking | Prevents audio overlap; stops system from transcribing daemon output as user input |
Use editor_logging_handler for debugging | Logs appear in the OpenHome dashboard without interrupting the user |
Using session_tasks.sleep()
Reading conversation history
Daemons can access the full message history to analyze conversations:- User profiling — Extract preferences, interests, recurring topics
- Sentiment tracking — Monitor emotional tone over time
- Context building — Accumulate information for RAG systems
- Trigger detection — Watch for specific keywords or phrases
Interrupting the conversation
If your daemon needs to speak or play audio, you must first callsend_interrupt_signal():
Daemon initialization
Note the different signature for daemoncall():
- Accepts
background_daemon_modeparameter - Passes
self.worker(notself) toCapabilityWorker - Entry point is named
first_function()by convention (can be any name)
SDK methods used
| Method | Purpose |
|---|---|
get_full_message_history() | Returns list of all messages in the current conversation |
session_tasks.sleep() | Sleep for N seconds with proper session cleanup |
send_interrupt_signal() | Pause main conversation before daemon speaks/plays audio |
speak() | Synthesize text to speech (call after send_interrupt_signal()) |
play_from_audio_file() | Play audio file (call after send_interrupt_signal()) |
Real-world examples
Build on this template to create:- Meeting summarizer — Analyze conversation every 5 minutes, extract action items
- User profiler — Build a profile of user interests, preferences, communication style
- Keyword alerter — Watch for specific words and fire notifications
- Sentiment monitor — Track emotional tone and offer support when needed
- RAG builder — Accumulate context for smarter future responses
- Pomodoro timer — Track work intervals and play break notifications
- Baby monitor — Listen for crying, alert user via interrupt
- Smart home scheduler — Poll device states, trigger automations
Combining with Skills
For advanced use cases, pair a daemon with a skill using shared file storage. See the Alarm template for a complete example.Next steps
Alarm Template
See how Skills and Daemons work together
File Storage
Coordinate between Skills and Daemons with shared files
Audio Playback
Play sounds and music from background daemons
Ability Types
Learn about Skills, Daemons, and Local abilities
