Cross-platform system tray agent with plugin architecture for triggers and actions.
- 🔌 Plugin Architecture — Extensible trigger and action system
- 🏗️ Clean Architecture — Dependency Injection and EventBus for decoupled design
- 🖥️ Cross-Platform — Works on macOS, Windows, and Linux
- 🌐 Server Integration — HTTP triggers and WebSocket actions via Centrifugo
- 🎯 System Tray — Background agent with tray icon
- 🔄 Robust Error Handling — Automatic retry with exponential backoff
- ✅ Well Tested — 313 tests with 97% coverage on core components
- 📦 Bundled Apps — Single executable with PyInstaller
Agimate Desktop uses modern software architecture patterns:
- Dependency Injection - Components receive dependencies via constructor
- EventBus - Pub/sub pattern for decoupled communication
- Protocol Interfaces - Type-safe contracts between components
- Async-First - Non-blocking I/O throughout the application
See CLAUDE.md for detailed architecture documentation.
# Clone the repository
git clone <repository-url>
cd agimate-desktop
# Install dependencies
pip install -e ".[server]"# Run the agent
python main.pyThe application will:
- Build the DI container
- Initialize all components
- Load and start plugins
- Connect to the server (if configured)
- Show system tray icon
# Install development dependencies
pip install -e ".[test,server]"The project uses pytest with comprehensive test coverage (313 tests).
# Run all tests with coverage
pytest
# Run tests with verbose output
pytest -v
# Run tests with coverage report
pytest --cov=core --cov=ui --cov=plugins --cov-report=term-missing# Run tests for a specific module
pytest tests/test_application.py
# Run tests for multiple modules
pytest tests/test_application.py tests/test_event_bus.py
# Run a specific test class
pytest tests/test_application.py::TestApplicationInit
# Run a specific test function
pytest tests/test_application.py::TestApplicationInit::test_init# Generate HTML coverage report
pytest --cov=core --cov=ui --cov=plugins --cov-report=html
# Open HTML report (macOS)
open htmlcov/index.html
# Open HTML report (Linux)
xdg-open htmlcov/index.html
# Open HTML report (Windows)
start htmlcov/index.html# Run tests with different verbosity levels
pytest -v # Verbose
pytest -vv # More verbose
pytest -q # Quiet
# Stop on first failure
pytest -x
# Show local variables in tracebacks
pytest -l
# Run only tests that failed last time
pytest --lf
# Run tests in parallel (requires pytest-xdist)
pytest -n autoCurrent coverage for core components:
- Application: 97% ⭐
- DI Container: 88% ⭐
- EventBus: 92% ⭐
- Protocols: 100% ⭐
- Plugin Manager: 82% ✅
- Server Client: 80% ✅
- Config Manager: 100% ⭐
Project aims for 80%+ coverage on all core modules.
agimate-desktop/
├── main.py # Entry point with DI architecture
├── core/ # Core system modules
│ ├── application.py # Application coordinator
│ ├── di_container.py # Dependency injection container
│ ├── event_bus.py # EventBus for pub/sub communication
│ ├── protocols.py # Protocol interfaces for DI
│ ├── config_manager.py # Configuration management
│ ├── device_info.py # Device identification
│ ├── plugin_manager.py # Plugin lifecycle
│ ├── server_client.py # HTTP + WebSocket client
│ ├── retry.py # Retry logic with exponential backoff
│ ├── models.py # Data models
│ └── paths.py # Path utilities
├── ui/ # User interface
│ ├── tray.py # System tray manager
│ └── settings.py # Settings window
├── plugins/ # Plugin implementations
│ ├── triggers/ # Trigger plugins
│ │ ├── file_watcher/ # File system monitoring
│ │ └── visual_buttons/ # Manual trigger buttons
│ └── actions/ # Action plugins
│ ├── show_notification/ # Notifications
│ └── tts/ # Text-to-speech
└── tests/ # Comprehensive test suite (313 tests)
├── conftest.py # Shared fixtures
├── test_application.py # Application tests
├── test_di_container.py # DI container tests
├── test_event_bus.py # EventBus tests
└── ... # More tests
# Build with PyInstaller
python build.py all# On Linux (native)
./build_appimage.sh
# On macOS/Windows (via Docker)
./build_appimage.shOutput:
- macOS:
dist/AgimateDesktop.app - Windows/Linux:
dist/AgimateDesktop - Linux AppImage:
dist/AgimateDesktop-x86_64.AppImage
Configuration is stored in platform-specific locations:
- macOS:
~/Library/Application Support/AgimateDesktop/config.json - Windows:
%LOCALAPPDATA%/AgimateDesktop/config.json - Linux:
~/.config/agimatedesktop/config.json
Example configuration:
{
"server_url": "http://localhost:8080",
"api_key": "your-api-key",
"auto_connect": true,
"reconnect_interval": 5000,
"log_level": "INFO"
}See PLUGINS.md for comprehensive plugin development guide.
from core.plugin_base import TriggerPlugin
class MyTrigger(TriggerPlugin):
@property
def name(self) -> str:
return "My Trigger"
async def initialize(self) -> None:
# Initialize plugin
self.interval = self.get_config("interval", 60)
async def start(self) -> None:
# Start monitoring
self._running = True
while self._running:
# Detect event
if self._check_condition():
self.emit_event("device.my.event", {"data": "value"})
await asyncio.sleep(self.interval)
async def stop(self) -> None:
# Stop monitoring
self._running = False
async def shutdown(self) -> None:
# Cleanup
await self.stop()from core.plugin_base import ActionPlugin
class MyAction(ActionPlugin):
@property
def name(self) -> str:
return "My Action"
def get_supported_actions(self) -> list[str]:
return ["MY_CUSTOM_ACTION"]
async def execute(self, action_type: str, parameters: dict) -> bool:
if action_type == "MY_CUSTOM_ACTION":
message = parameters.get("message")
# Perform action
print(f"Executing: {message}")
return True
return FalseComponents are created and wired by the DI Container:
# main.py
container = ContainerBuilder.build_container(app, loop)
application = Application(
config_manager=container.get("config_manager"),
plugin_manager=container.get("plugin_manager"),
server_client=container.get("server_client"),
event_bus=container.get("event_bus"),
# ... other components
)Components communicate via EventBus (pub/sub pattern):
- Plugin events → EventBus → Application → Server
- Server actions → EventBus → Application → Plugins
- UI events → EventBus → Application
Network operations automatically retry with exponential backoff:
- HTTP requests: 3 attempts, exponential delay
- WebSocket reconnect: 10 attempts max
- Transient errors (5xx, timeouts) → retry
- Permanent errors (4xx) → fail fast
Settings and plugin configs are validated before use:
- Invalid configs → plugin disabled with error message
- Clear validation errors in logs
- Graceful degradation on failures
- CLAUDE.md - Architecture, conventions, and technical details
- PLUGINS.md - Complete plugin development guide
- tests/ - Examples of testing patterns
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Ensure tests pass:
pytest - Ensure coverage:
pytest --cov=core --cov=ui - Submit a pull request
# Run tests during development
pytest -v
# Run tests with coverage
pytest --cov=core --cov=ui --cov-report=term-missing
# Run tests in watch mode (requires pytest-watch)
ptw
# Check specific module
pytest tests/test_application.py -vMIT License
For issues and questions, please use the GitHub issue tracker.
- ✨ Implemented Dependency Injection architecture
- ✨ Added EventBus for pub/sub communication
- ✨ Protocol interfaces for type-safe contracts
- ✨ Automatic retry with exponential backoff
- ✨ Comprehensive config validation
- ✨ 313 tests with 97% coverage on core
- 📚 Complete documentation (CLAUDE.md, PLUGINS.md)
- 🔧 Improved error handling and logging