""" Plugin Name Brief description of what this plugin does. API Version: 1.0.0 """ from src.plugin_system.base_plugin import BasePlugin from PIL import Image from typing import Dict, Any, Optional import logging import time class PluginClassName(BasePlugin): """ Plugin class that inherits from BasePlugin. This plugin demonstrates the basic structure and common patterns for LEDMatrix plugins. """ def __init__( self, plugin_id: str, config: Dict[str, Any], display_manager, cache_manager, plugin_manager, ): """Initialize the plugin.""" super().__init__(plugin_id, config, display_manager, cache_manager, plugin_manager) # Initialize plugin-specific data self.data = None self.last_update_time = None # Load configuration values self.api_key = config.get("api_key", "") self.refresh_interval = config.get("refresh_interval", 60) self.logger.info(f"Plugin {plugin_id} initialized") def update(self) -> None: """ Fetch/update data for this plugin. This method is called periodically based on update_interval specified in the manifest. Use cache_manager to avoid excessive API calls. """ cache_key = f"{self.plugin_id}_data" # Check cache first cached = self.cache_manager.get(cache_key, max_age=self.refresh_interval) if cached: self.data = cached self.logger.debug("Using cached data") return try: # Fetch new data self.data = self._fetch_data() # Cache the data self.cache_manager.set(cache_key, self.data, ttl=self.refresh_interval) self.last_update_time = time.time() self.logger.info("Data updated successfully") except Exception as e: self.logger.error(f"Failed to update data: {e}") # Use cached data if available, even if expired # Use a very large max_age (1 year) to effectively bypass expiration for fallback expired_cached = self.cache_manager.get(cache_key, max_age=31536000) if expired_cached: self.data = expired_cached self.logger.warning("Using expired cache due to update failure") def display(self, force_clear: bool = False) -> None: """ Render this plugin's display. Args: force_clear: If True, clear display before rendering """ if force_clear: self.display_manager.clear() # Check if we have data to display if not self.data: self._display_error("No data available") return try: # Render plugin content self._render_content() # Update the display self.display_manager.update_display() except Exception as e: self.logger.error(f"Display error: {e}") self._display_error("Display error") def _fetch_data(self) -> Dict[str, Any]: """ Fetch data from external source. Returns: Dictionary containing fetched data """ # TODO: Implement data fetching logic # Example: # import requests # response = requests.get("https://api.example.com/data", # headers={"Authorization": f"Bearer {self.api_key}"}) # return response.json() # Placeholder return { "message": "Hello, World!", "timestamp": time.time() } def _render_content(self) -> None: """Render the plugin content on the display.""" # Get display dimensions width = self.display_manager.width height = self.display_manager.height # Example: Draw text text = self.data.get("message", "No data") x = 5 y = height // 2 self.display_manager.draw_text( text, x=x, y=y, color=(255, 255, 255) # White ) # Example: Draw image # if hasattr(self, 'logo_image'): # self.display_manager.draw_image( # self.logo_image, # x=0, # y=0 # ) def _display_error(self, message: str) -> None: """Display an error message.""" self.display_manager.clear() width = self.display_manager.width height = self.display_manager.height self.display_manager.draw_text( message, x=5, y=height // 2, color=(255, 0, 0) # Red ) self.display_manager.update_display() def validate_config(self) -> bool: """ Validate plugin configuration. Returns: True if config is valid, False otherwise """ # Call parent validation first if not super().validate_config(): return False # Add custom validation # Example: Check for required API key # if self.config.get("require_api_key", True): # if not self.api_key: # self.logger.error("API key is required but not provided") # return False return True def has_live_content(self) -> bool: """ Check if plugin has live content to display. Override this method to enable live priority features. Returns: True if plugin has live content, False otherwise """ # Example: Check if there's live data # return self.data and self.data.get("is_live", False) return False def get_info(self) -> Dict[str, Any]: """ Return plugin info for display in web UI. Returns: Dictionary with plugin information """ info = super().get_info() # Add plugin-specific info info.update({ "data_available": self.data is not None, "last_update": self.last_update_time, # Add more info as needed }) return info def cleanup(self) -> None: """Cleanup resources when plugin is unloaded.""" # Clean up any resources (threads, connections, etc.) # Example: # if hasattr(self, 'api_client'): # self.api_client.close() super().cleanup()