- docs: scope the self.layout note to BasePlugin subclasses (others build a LayoutContext directly) and make explicit that adaptive layout is opt-in — classic rendering stays unless a plugin adopts the APIs. - dev_server: broaden the render-request catch (a bad manifest.json now returns a clean 400 instead of an unhandled 500) and stop echoing raw exception text in the loader-failure responses — full tracebacks go to the dev server's console log instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
20 KiB
LEDMatrix Plugin Development Guide
This guide explains how to set up a development workflow for plugins that are maintained in separate Git repositories while still being able to test them within the LEDMatrix project.
Rendering guidance: plugins should read the display size dynamically (
self.display_manager.matrix.width/height) rather than hardcoding one panel. For plugins that want to scale their layout to any panel, the opt-in adaptive layout system (ADAPTIVE_LAYOUT.md) provides the shared helpers — fonts, images, and composite layouts that scale. Existing plugins keep their classic rendering unless they adopt those APIs; nothing migrates automatically.
Overview
When developing plugins in separate repositories, you need a way to:
- Test plugins within the LEDMatrix project
- Make changes and commit them back to the plugin repository
- Avoid git conflicts between LEDMatrix and plugin repositories
- Easily switch between development and production modes
The solution uses symbolic links to connect plugin repositories to the plugins/ directory, combined with a helper script to manage the linking process.
Plugin directory note: the dev workflow described here puts symlinks in
plugins/. The plugin loader's production default isplugin-repos/(set byplugin_system.plugins_directoryinconfig.json). Importantly, the main discovery path (PluginManager.discover_plugins()) only scans the configured directory — it does not fall back toplugins/. Two narrower paths do: the Plugin Store install/update logic instore_manager.py, andschema_manager.get_schema_path()(which the web UI form generator uses to findconfig_schema.json). That's why plugins installed via the Plugin Store still work even with symlinks inplugins/, but your own dev plugin won't appear in the rotation until you either move it toplugin-repos/or changeplugin_system.plugins_directorytopluginsin the General tab of the web UI. The latter is the smoother dev setup.
Quick Start
1. Link a Plugin from GitHub
The easiest way to link a plugin that's already on GitHub:
./scripts/dev/dev_plugin_setup.sh link-github music
This will:
- Clone
https://github.com/ChuckBuilds/ledmatrix-music.gitto~/.ledmatrix-dev-plugins/ledmatrix-music - Create a symbolic link from
plugins/musicto the cloned repository - Validate that the plugin has a proper
manifest.json
2. Link a Local Plugin Repository
If you already have a plugin repository cloned locally:
./scripts/dev/dev_plugin_setup.sh link music ../ledmatrix-music
This creates a symlink from plugins/music to your local repository path.
3. Check Status
See which plugins are linked and their git status:
./scripts/dev/dev_plugin_setup.sh status
4. Work on Your Plugin
cd plugins/music # Actually editing the linked repository
# Make your changes
git add .
git commit -m "feat: add new feature"
git push origin main
5. Update Plugins
Pull latest changes from remote:
# Update all linked plugins
./scripts/dev/dev_plugin_setup.sh update
# Or update a specific plugin
./scripts/dev/dev_plugin_setup.sh update music
6. Unlink When Done
Remove the symlink (repository is preserved):
./scripts/dev/dev_plugin_setup.sh unlink music
Detailed Commands
link <plugin-name> <repo-path>
Links a local plugin repository to the plugins directory.
Arguments:
plugin-name: The name of the plugin (will be the directory name inplugins/)repo-path: Path to the plugin repository (absolute or relative)
Example:
./scripts/dev/dev_plugin_setup.sh link football-scoreboard ../ledmatrix-football-scoreboard
Notes:
- The script validates that the repository contains a
manifest.jsonfile - If a plugin directory already exists, you'll be prompted to replace it
- The repository path can be absolute or relative
link-github <plugin-name> [repo-url]
Clones a plugin from GitHub and links it.
Arguments:
plugin-name: The name of the plugin (will be the directory name inplugins/)repo-url: (Optional) Full GitHub repository URL. If omitted, constructs from pattern:https://github.com/ChuckBuilds/ledmatrix-<plugin-name>.git
Examples:
# Auto-construct URL from plugin name
./scripts/dev/dev_plugin_setup.sh link-github music
# Use explicit URL
./scripts/dev/dev_plugin_setup.sh link-github stocks https://github.com/ChuckBuilds/ledmatrix-stocks.git
# Link from a different GitHub user
./scripts/dev/dev_plugin_setup.sh link-github custom-plugin https://github.com/OtherUser/custom-plugin.git
Notes:
- Repositories are cloned to
~/.ledmatrix-dev-plugins/by default (configurable) - If the repository already exists, it will be updated with
git pullinstead of re-cloning - The cloned repository is preserved when you unlink the plugin
unlink <plugin-name>
Removes the symlink for a plugin.
Arguments:
plugin-name: The name of the plugin to unlink
Example:
./scripts/dev/dev_plugin_setup.sh unlink music
Notes:
- Only removes the symlink, does NOT delete the repository
- Your work and git history are preserved in the repository location
list
Lists all plugins in the plugins/ directory and shows their status.
Example:
./scripts/dev/dev_plugin_setup.sh list
Output:
- ✓ Green checkmark: Plugin is symlinked (development mode)
- ○ Yellow circle: Plugin is a regular directory (production/installed mode)
- Shows the source path for symlinked plugins
- Shows git status (branch, clean/dirty) for linked repos
status
Shows detailed status of all linked plugins.
Example:
./scripts/dev/dev_plugin_setup.sh status
Shows:
- Link status (working/broken)
- Repository path
- Git branch
- Remote URL
- Git status (clean, uncommitted changes, ahead/behind remote)
- Summary of all plugins
update [plugin-name]
Updates plugin(s) by running git pull in their repositories.
Arguments:
plugin-name: (Optional) Specific plugin to update. If omitted, updates all linked plugins.
Examples:
# Update all linked plugins
./scripts/dev/dev_plugin_setup.sh update
# Update specific plugin
./scripts/dev/dev_plugin_setup.sh update music
Configuration
Custom Development Directory
By default, GitHub repositories are cloned to ~/.ledmatrix-dev-plugins/. You can customize this by creating a dev_plugins.json file:
{
"dev_plugins_dir": "/path/to/your/dev/plugins",
"github_user": "ChuckBuilds",
"github_pattern": "ledmatrix-",
"plugins": {
"music": {
"source": "github",
"url": "https://github.com/ChuckBuilds/ledmatrix-music.git",
"branch": "main"
}
}
}
Configuration options:
dev_plugins_dir: Where to clone GitHub repositories (default:~/.ledmatrix-dev-plugins)github_user: Default GitHub username for auto-constructing URLsgithub_pattern: Pattern for repository names (default:ledmatrix-)plugins: Plugin definitions (optional, for future auto-discovery features)
Note: Copy dev_plugins.json.example to dev_plugins.json and customize it. The dev_plugins.json file is git-ignored.
Development Workflow
Typical Development Session
-
Link your plugin for development:
./scripts/dev/dev_plugin_setup.sh link-github music -
Test in LEDMatrix:
# Run LEDMatrix with your plugin python run.py -
Make changes:
cd plugins/music # Edit files... # Test changes... -
Commit to plugin repository:
cd plugins/music # This is actually your repo git add . git commit -m "feat: add new feature" git push origin main -
Update from remote (if needed):
./scripts/dev/dev_plugin_setup.sh update music -
When done developing:
./scripts/dev/dev_plugin_setup.sh unlink music
Working with Multiple Plugins
You can have multiple plugins linked simultaneously:
./scripts/dev/dev_plugin_setup.sh link-github music
./scripts/dev/dev_plugin_setup.sh link-github stocks
./scripts/dev/dev_plugin_setup.sh link-github football-scoreboard
# Check status of all
./scripts/dev/dev_plugin_setup.sh status
# Update all at once
./scripts/dev/dev_plugin_setup.sh update
Switching Between Development and Production
Development mode: Plugins are symlinked to your repositories
- Edit files directly in
plugins/<name> - Changes are in the plugin repository
- Git operations work normally
Production mode: Plugins are installed normally
- Plugins are regular directories (installed via plugin store or manually)
- Can't edit directly (would need to edit in place or re-install)
- Use
unlinkto remove symlink if you want to switch back to installed version
Best Practices
1. Keep Repositories Outside LEDMatrix
The script clones GitHub repositories to ~/.ledmatrix-dev-plugins/ by default, which is outside the LEDMatrix directory. This:
- Avoids git conflicts
- Keeps plugin repos separate from LEDMatrix repo
- Makes it easy to manage multiple plugin repositories
2. Use Descriptive Commit Messages
When committing changes in your plugin repository, use clear commit messages following the project's conventions:
git commit -m "feat(music): add album art support"
git commit -m "fix(stocks): resolve API timeout issue"
3. Test Before Committing
Always test your plugin changes in LEDMatrix before committing:
# Make changes
cd plugins/music
# ... edit files ...
# Test in LEDMatrix
cd ../..
python run.py
# If working, commit
cd plugins/music
git add .
git commit -m "feat: new feature"
4. Keep Plugins Updated
Regularly update your linked plugins to get the latest changes:
./scripts/dev/dev_plugin_setup.sh update
5. Check Status Regularly
Before starting work, check the status of your linked plugins:
./scripts/dev/dev_plugin_setup.sh status
This helps you:
- See if you have uncommitted changes
- Check if you're behind the remote
- Identify any broken symlinks
Troubleshooting
Plugin Not Discovered by LEDMatrix
If LEDMatrix doesn't discover your linked plugin:
-
Check the symlink exists:
ls -la plugins/your-plugin-name -
Verify manifest.json exists:
ls plugins/your-plugin-name/manifest.json -
Check PluginManager logs:
- LEDMatrix logs should show plugin discovery
- Look for errors related to the plugin
Broken Symlink
If a symlink is broken (target repository was moved or deleted):
-
Check status:
./scripts/dev/dev_plugin_setup.sh status -
Unlink and re-link:
./scripts/dev/dev_plugin_setup.sh unlink plugin-name ./scripts/dev/dev_plugin_setup.sh link-github plugin-name
Git Conflicts
If you have conflicts when updating:
-
Manually resolve in the plugin repository:
cd ~/.ledmatrix-dev-plugins/ledmatrix-music git pull # Resolve conflicts... git add . git commit -
Or use the update command:
./scripts/dev/dev_plugin_setup.sh update music
Plugin Directory Already Exists
If you try to link a plugin but the directory already exists:
-
Check if it's already linked:
./scripts/dev/dev_plugin_setup.sh list -
If it's a symlink to the same location, you're done
-
If it's a regular directory or different symlink:
- The script will prompt you to replace it
- Or manually backup:
mv plugins/plugin-name plugins/plugin-name.backup
Advanced Usage
Linking Plugins from Different GitHub Users
./scripts/dev/dev_plugin_setup.sh link-github custom-plugin https://github.com/OtherUser/custom-plugin.git
Using a Custom Development Directory
Create dev_plugins.json:
{
"dev_plugins_dir": "/home/user/my-dev-plugins"
}
Combining Local and GitHub Plugins
You can mix local and GitHub plugins:
# Link from GitHub
./scripts/dev/dev_plugin_setup.sh link-github music
# Link local repository
./scripts/dev/dev_plugin_setup.sh link custom-plugin ../my-custom-plugin
Integration with Plugin Store
The development workflow is separate from the plugin store installation:
- Plugin Store: Installs plugins to
plugins/as regular directories - Development Setup: Links plugin repositories as symlinks
If you install a plugin via the store, you can still link it for development:
# Store installs to plugins/music (regular directory)
# Link for development (will prompt to replace)
./scripts/dev/dev_plugin_setup.sh link-github music
When you unlink, the directory is removed. If you want to switch back to the store version, re-install it via the plugin store.
API Reference
When developing plugins, you'll need to use the APIs provided by the LEDMatrix system:
- Plugin API Reference - Complete reference for Display Manager, Cache Manager, and Plugin Manager methods
- Advanced Plugin Development - Advanced patterns, examples, and best practices
- Developer Quick Reference - Quick reference for common developer tasks
Key APIs for Plugin Developers
Display Manager (self.display_manager):
clear(),update_display()- Core display operationsdraw_text()- Text rendering. For images, paste directly ontodisplay_manager.image(a PIL Image) and callupdate_display(); there is nodraw_image()helper method.draw_weather_icon(),draw_sun(),draw_cloud()- Weather iconsget_text_width(),get_font_height()- Text utilitiesset_scrolling_state(),defer_update()- Scrolling state management
Cache Manager (self.cache_manager):
get(),set(),delete()- Basic cachingget_cached_data_with_strategy()- Advanced caching with strategiesget_background_cached_data()- Background service caching
Plugin Manager (self.plugin_manager):
get_plugin(),get_all_plugins()- Access other pluginsget_plugin_info()- Get plugin information
See PLUGIN_API_REFERENCE.md for complete documentation.
3rd Party Plugin Development
Want to create and share your own plugin? Here's everything you need to know.
Getting Started
-
Review the documentation:
- Plugin Architecture Spec - System architecture
- Plugin API Reference - Available methods
- Advanced Plugin Development - Patterns and examples
-
Start with a template:
- Use the Hello World plugin as a starting point
- Or fork an existing plugin and modify it
-
Follow the plugin structure:
your-plugin/ ├── manifest.json # Required: Plugin metadata ├── manager.py # Required: Plugin class ├── config_schema.json # Recommended: Configuration schema ├── requirements.txt # Optional: Python dependencies └── README.md # Recommended: User documentation
Plugin Requirements
Your plugin must:
-
Inherit from BasePlugin:
from src.plugin_system.base_plugin import BasePlugin class MyPlugin(BasePlugin): def update(self): # Fetch data pass def display(self, force_clear=False): # Render display pass -
Include manifest.json with required fields:
{ "id": "my-plugin", "name": "My Plugin", "version": "1.0.0", "class_name": "MyPlugin", "entry_point": "manager.py", "display_modes": ["my_plugin"], "compatible_versions": [">=2.0.0"] } -
Match class name: The class name in
manager.pymust matchclass_namein manifest
Testing Your Plugin
-
Test locally:
# Link your plugin for development ./scripts/dev/dev_plugin_setup.sh link your-plugin /path/to/your-plugin # Run LEDMatrix with emulator python run.py --emulator -
Test on hardware: Deploy to Raspberry Pi and test on actual LED matrix
-
Use mocks for unit testing: See Advanced Plugin Development
Versioning Best Practices
- Use semantic versioning:
MAJOR.MINOR.PATCH(e.g.,1.2.3) - Automatic version bumping: Use the pre-push git hook for automatic patch version bumps
- Manual versioning: Only needed for major/minor bumps or special cases
- GitHub as source of truth: Plugin store fetches versions from GitHub releases/tags/manifest
See the Git Workflow rules for version management details.
Submitting to Official Registry
To have your plugin added to the official plugin store:
-
Ensure quality:
- Plugin works reliably
- Well-documented (README.md)
- Follows best practices
- Tested on Raspberry Pi hardware
-
Create GitHub repository:
- Repository name:
ledmatrix-<plugin-name> - Public repository
- Proper README.md with installation instructions
- Repository name:
-
Contact maintainers:
- Open a GitHub issue in the ledmatrix-plugins repository
- Or reach out on Discord: https://discord.gg/uW36dVAtcT
- Include: Repository URL, plugin description, why it's useful
-
Review process:
- Code review for quality and security
- Testing on Raspberry Pi hardware
- Documentation review
- If approved, added to official registry
Plugin Store Integration Requirements
For your plugin to work well in the plugin store:
- GitHub repository: Must be publicly accessible on GitHub
- Releases or tags: Recommended for version tracking
- README.md: Clear installation and configuration instructions
- config_schema.json: Recommended for web UI configuration
- manifest.json: Required with all required fields
- requirements.txt: If your plugin has Python dependencies
Distribution Options
-
Official Registry (Recommended):
- Listed in default plugin store
- Automatic updates
- Verified badge
- Requires approval
-
Custom Repository:
- Host your own plugin repository
- Users can install via "Install from GitHub" in web UI
- Full control over distribution
-
Direct Installation:
- Users can clone and install manually
- Good for development/testing
Best Practices for 3rd Party Plugins
- Documentation: Include comprehensive README.md
- Configuration: Provide config_schema.json for web UI
- Error handling: Graceful failures with clear error messages
- Logging: Use plugin logger for debugging
- Testing: Test on actual Raspberry Pi hardware
- Versioning: Follow semantic versioning
- Dependencies: Minimize external dependencies
- Performance: Optimize for Pi's limited resources
See Also
- Plugin Architecture Specification - Complete system specification
- Plugin API Reference - Complete API documentation
- Advanced Plugin Development - Advanced patterns and examples
- Plugin Quick Reference - Quick development reference
- Plugin Configuration Guide - Configuration setup
- Plugin Store User Guide - Using the plugin store