Over the past year, I've been building a comprehensive home automation system using AppDaemon, a Python framework for Home Assistant automation. What started as simple light controls has evolved into a sophisticated ecosystem of 30+ interconnected applications managing everything from CPAP machines to interactive language learning games for my child.
Why AppDaemon?
Home Assistant's built-in automations are great for simple rules, but when you need complex logic, state management, or want to reuse code across multiple automations, AppDaemon shines. It provides:
- Full Python programming capabilities
- Object-oriented design with inheritance
- Async/await support for concurrent operations
- Direct access to Home Assistant's state and service APIs
- Easy testing with pytest
The Architecture: Base Class Pattern
At the core of my system is a BaseApp
class that all automations inherit from:
class BaseApp(hass.Hass):
ArgClass=None
def __init__(self, ad, name, logging, args, config, app_config, global_vars):
super().__init__(ad, name, logging, args, config, app_config, global_vars)
assert self.ArgClass is not None
self.statsd=statsd.StatsClient("statsd.lan", 9125)
self.instance_name = name
self.filtered_args = {k:v for k,v in self.args.items() if k in fields_}
self.input = self.ArgClass(**self.filtered_args)
This pattern provides several benefits:
- Type Safety: Using dataclasses for arguments ensures type checking and validation
- Metrics Collection: Built-in StatsD integration for monitoring
- Consistent Logging: Standardized logging across all apps
- Code Reuse: Common functionality available to all automations
Real-World Applications
1. CPAP Light Synchronization
One of the more unique automations synchronizes bedroom lighting with my CPAP machine. When the CPAP starts (detected via power monitoring), the lights gradually dim to create ideal sleeping conditions:
class CPAPLightSync(BaseApp):
async def initialize(self):
await self.listen_state(
self.cpap_state_changed,
self.input.cpap_sensor,
attribute="state"
)
async def cpap_state_changed(self, entity, attribute, old, new, *_):
if float(new) > 5: # CPAP is running
await self.turn_off("light.bedroom", transition=30)
2. Multi-Language Learning Game
Perhaps the most ambitious application is an interactive language learning system for my son Luca. It integrates a smart button (Hatch Baby Rest) with text-to-speech to teach vocabulary in multiple languages:
@dataclass
class LucaMultiLanguageLearningArgs:
hatch_button: str
media_player: str
dictionary_path: str
available_languages: Dict[str, Dict[str, str]]
current_language: str = "french"
The system:
- Loads JSON dictionaries for French, Spanish, Farsi, and other languages
- Tracks recently used words to avoid repetition
- Speaks word pairs (foreign word → English translation)
- Plays Spotify playlists during specific hours
- Handles Unicode characters gracefully for non-Latin scripts
3. Motion-Activated Devices
The MotionActivatedDevice
class demonstrates the power of abstraction:
class MotionActivatedDevice(BaseApp):
async def motion_detected(self, entity, attribute, old, new, *_):
if new == "on" and self.should_activate():
await self.activate_device()
self.schedule_deactivation()
This single class powers multiple automations:
- Bathroom lights that stay on longer during nighttime
- Office fans that run when occupied
- Display screens that wake on approach
4. Smart Environmental Control
The CarbonDioxydeRegulator
monitors CO₂ levels and automatically controls ventilation:
async def regulate_co2(self, entity, attribute, old, new, *_):
co2_level = float(new)
if co2_level > 1000:
await self.turn_on("fan.office")
elif co2_level < 800:
await self.turn_off("fan.office")
Testing Infrastructure
Quality is ensured through comprehensive testing. The repository includes pytest-based tests that validate argument parsing, state changes, and service calls:
def test_args_types():
for app in apps:
module = importlib.import_module(f"apps.{app}")
assert hasattr(module.ArgClass, '__dataclass_fields__')
Enhanced Git Hooks for Quality
To maintain code quality, I've implemented a sophisticated pre-commit hook that goes beyond simple test execution. The hook provides:
- Visual Progress Indicators: Green checkmarks (✓) for passing checks, red crosses (✗) for failures
- Staged Test Results: Shows exactly which tests passed or failed with formatted output
- Syntax Validation: Checks all Python files for syntax errors before commit
- Debug Statement Detection: Warns about leftover print statements or pdb imports
- Detailed Error Reporting: Highlights file paths and line numbers in test failures
The hook transforms the typical pytest output into a developer-friendly format that makes it immediately obvious what needs attention.
Metrics and Observability
Every automation reports metrics to a StatsD server, enabling monitoring of:
- Automation execution frequency
- State change patterns
- Error rates and types
- Performance metrics
This data feeds into Grafana dashboards, providing insights into home patterns and automation effectiveness.
Key Learnings
After a year of development, here are the key takeaways:
- Start with a solid foundation: The BaseApp pattern has saved countless hours
- Type hints are invaluable: Dataclasses catch configuration errors early
- Test everything: Automated tests prevent regressions when updating complex automations
- Monitor everything: Metrics reveal patterns you didn't know existed
- Keep it maintainable: Clear naming and documentation matter when returning to code months later
Looking Forward
The system continues to evolve. Upcoming projects include:
- Voice-activated scene controls using local speech recognition
- Predictive heating/cooling based on weather forecasts and historical patterns
- Integration with security cameras for person detection
- Automated plant watering based on soil moisture sensors
Conclusion
AppDaemon has transformed my home into a responsive environment that adapts to our family's needs. From ensuring optimal sleep conditions to helping my son learn new languages, these automations have become an invisible but essential part of daily life.
The combination of Python's flexibility, Home Assistant's ecosystem, and AppDaemon's framework creates a powerful platform for home automation that goes far beyond simple "if this then that" rules.
The full source code is available on GitHub, and I welcome contributions and questions from the community. Whether you're looking to automate a single room or your entire home, I hope these patterns and examples help you build something amazing.
Have questions about specific automations or want to share your own AppDaemon projects? Feel free to reach out or open an issue on the repository.
Top comments (0)