PLUGIN_DEVELOPMENT_GUIDE.md
- Added a "Plugin directory note" callout near the top explaining
the plugins/ vs plugin-repos/ split:
- Dev workflow uses plugins/ (where dev_plugin_setup.sh creates
symlinks)
- Production / Plugin Store uses plugin-repos/ (the configurable
default per config.template.json:130)
- The plugin loader falls back to plugins/ so dev symlinks are
picked up automatically (schema_manager.py:77)
- User can set plugins_directory to "plugins" in the General tab
if they want both to share a directory
CLAUDE.md
- The Project Structure section had plugins/ and plugin-repos/
exactly reversed:
- Old: "plugins/ - Installed plugins directory (gitignored)"
"plugin-repos/ - Development symlinks to monorepo plugin dirs"
- Real: plugin-repos/ is the canonical Plugin Store install
location and is not gitignored. plugins/* IS gitignored
(verified in .gitignore) and is the legacy/dev location used by
scripts/dev/dev_plugin_setup.sh.
Reversed the descriptions and added line refs.
systemd/README.md
- "Manual Installation" section told users to copy the unit file
directly to /etc/systemd/system/. Verified the unit file in
systemd/ledmatrix.service contains __PROJECT_ROOT_DIR__
placeholders that the install scripts substitute at install time.
A user following the manual steps would get a service that fails
to start with "WorkingDirectory=__PROJECT_ROOT_DIR__" errors.
Added a clear warning and a sed snippet that substitutes the
placeholder before installing.
src/common/README.md
- Was missing 2 of the 11 utility modules in the directory
(verified with ls): permission_utils.py and cli.py. Added brief
descriptions for both.
Out-of-scope code bug found while auditing (flagged but not fixed):
- scripts/dev/dev_plugin_setup.sh:9 sets PROJECT_ROOT="$SCRIPT_DIR"
which resolves to scripts/dev/, not the project root. This means
the script's PLUGINS_DIR resolves to scripts/dev/plugins/ instead
of the project's plugins/ — confirmed by the existence of
scripts/dev/plugins/of-the-day/ from prior runs. Real fix is to
set PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)". Not fixing in
this docs PR.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
19 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.
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), but it falls back toplugins/so the dev symlinks are picked up automatically. The Plugin Store installs toplugin-repos/. If you want both your dev symlinks and store installs to share the same directory, setplugins_directorytopluginsin the General tab of the web UI.
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