Files
LEDMatrix/docs/PLUGIN_CONFIGURATION_TABS.md
ChuckandClaude Opus 5.5 09103a8a7d refactor(web): split api_v3/plugins.py by area (#658)
* refactor(web): split api_v3/plugins.py by area

web_interface/blueprints/api_v3/plugins.py (3,285 lines) becomes:
- plugins.py: installed list, enable/disable, plugin actions
- plugin_store.py: install, update, uninstall, store, saved repositories
- plugin_config.py: config get/save, schema, reset
- plugin_assets.py: asset uploads and plugin static files
- plugin_health.py: health, metrics, limits
- plugin_operations.py: operation history, state reconciliation
- plugin_calendar.py: calendar credentials and auth

Pure move: all 44 functions and 38 route decorators are byte-identical
(checked with ast), URLs and endpoint names are unchanged (url-map test).
Each module imports only what it uses. Tests and config.py that reached
into plugins.py for moved names now import from the new module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): keep exception text out of calendar responses; annotate moved code

The split made scanners report existing findings in the moved code as new:
- CodeQL: the calendar auth and calendar-list routes returned exception
  text (redacted, but still derived from the exception). Both now log the
  exception and return a fixed message pointing at the log.
- MD5 in the asset upload only makes a filename unique: usedforsecurity=False.
- pickle reads/writes the calendar plugin's own OAuth token (as before):
  annotated. Token-status labels and a log line naming the secrets path are
  false positives: annotated with the repo's nosec/nosemgrep convention.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): name uploaded assets with SHA-256 instead of MD5

The hash only makes an uploaded image's filename unique. Codacy flags MD5
even with usedforsecurity=False, and SHA-256 does the job as well; existing
files keep their names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): keep the redacted exception detail in calendar errors

Reverts the calendar part of 5e695b7c. The project's policy
(test_no_api_v3_handler_discards_its_exception) is that an API error
carries the redacted exception detail -- describe_exception runs it
through the credential redactor -- so a failure is diagnosable from the web
UI. Dropping it for CodeQL broke that; CodeQL can't see the redaction, so
its two alerts here are false positives, like the existing ones on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:58:53 -04:00

280 lines
8.0 KiB
Markdown

# Plugin Configuration Tabs
## Overview
Each installed plugin now gets its own dedicated configuration tab in the web interface. This provides a clean, organized way to configure plugins without cluttering the **Plugin Manager** tab.
## Features
- **Automatic Tab Generation**: When a plugin is installed, a new tab is automatically created in the web UI
- **JSON Schema-Based Forms**: Configuration forms are automatically generated based on each plugin's `config_schema.json`
- **Type-Safe Inputs**: Form inputs are created based on the JSON Schema type (boolean, number, string, array, enum)
- **Default Values**: All fields show current values or fallback to schema defaults
- **Real-Time Validation**: Input constraints from JSON Schema are enforced (min, max, maxLength, etc.)
## User Experience
### Accessing Plugin Configuration
1. Navigate to the **Plugin Manager** tab to see all installed plugins
2. Click the **Configure** button on any plugin card
3. You'll be automatically taken to that plugin's configuration tab
4. Alternatively, click directly on the plugin's tab button in the second nav row
### Configuring a Plugin
1. Open the plugin's configuration tab
2. Modify settings using the generated form
3. Click **Save Configuration**. The settings apply to the running display
without a restart: the display service reloads `config.json` when it
changes and calls the plugin's `on_config_change()`
The tab also has **Refresh** (reload the form), **Update** (update the
plugin) and **Uninstall** buttons.
### Plugin Manager vs Per-Plugin Configuration
- **Plugin Manager tab** (second nav row): used for browsing the
Plugin Store, installing plugins, toggling installed plugins on/off,
and updating/uninstalling them
- **Per-plugin tabs** (one per installed plugin, also in the second
nav row): used for configuring that specific plugin's behavior and
settings via a form auto-generated from its `config_schema.json`
## For Plugin Developers
### Requirements
Every installed plugin gets a tab. To get a generated form in it, include a
`config_schema.json` file in the plugin's directory. The name is fixed: the
web interface finds the schema by that file name (`SchemaManager` in
`src/plugin_system/schema_manager.py`), and no manifest field points to it.
**Note:** You can optionally specify a Font Awesome `icon` class for your
plugin tab in `manifest.json`. See [Plugin Custom Icons Guide](PLUGIN_CUSTOM_ICONS.md) for details.
### Supported JSON Schema Types
The form generator supports the following JSON Schema types:
#### Boolean
```json
{
"type": "boolean",
"default": true,
"description": "Enable or disable this feature"
}
```
Renders as: Toggle switch
#### Number / Integer
```json
{
"type": "integer",
"default": 60,
"minimum": 1,
"maximum": 300,
"description": "Update interval in seconds"
}
```
Renders as: Number input with min/max constraints
#### String
```json
{
"type": "string",
"default": "Hello, World!",
"minLength": 1,
"maxLength": 50,
"description": "The message to display"
}
```
Renders as: Text input with length constraints
#### Array
```json
{
"type": "array",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"default": [255, 255, 255],
"description": "RGB color [R, G, B]"
}
```
Renders as: Text input (comma-separated values)
Example input: `255, 128, 0`
#### Enum (Select)
```json
{
"type": "string",
"enum": ["small", "medium", "large"],
"default": "medium",
"description": "Display size"
}
```
Renders as: Dropdown select
### Example config_schema.json
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "My Plugin Configuration",
"description": "Configure my awesome plugin",
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"description": "Enable or disable this plugin"
},
"message": {
"type": "string",
"default": "Hello!",
"minLength": 1,
"maxLength": 50,
"description": "The message to display"
},
"update_interval": {
"type": "integer",
"default": 60,
"minimum": 1,
"maximum": 3600,
"description": "Update interval in seconds"
},
"color": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"default": [255, 255, 255],
"description": "RGB color [R, G, B]"
},
"mode": {
"type": "string",
"enum": ["scroll", "static", "fade"],
"default": "scroll",
"description": "Display mode"
}
},
"required": ["enabled"],
"additionalProperties": false
}
```
### Best Practices
1. **Use Descriptive Labels**: The `description` field is shown as help text under each input
2. **Set Sensible Defaults**: Always provide default values that work out of the box
3. **Use Constraints**: Leverage min/max, minLength/maxLength to guide users
4. **Mark Required Fields**: Use the `required` array in your schema
5. **Organize Properties**: List properties in order of importance
### Form Generation Process
Forms are rendered on the server, not generated in the browser:
1. The web UI loads installed plugins via `/api/v3/plugins/installed` and adds
a tab button for each one
2. Opening a tab loads `/v3/partials/plugin-config/<plugin_id>`
(`web_interface/blueprints/pages_v3.py`), which loads the plugin's schema
through `SchemaManager` and its current values from `config.json`
3. `web_interface/templates/v3/partials/plugin_config.html` renders the form
from the schema (widgets named by `x-widget` are rendered by the scripts in
`web_interface/static/v3/js/widgets/`)
4. **Save Configuration** posts the form to `/api/v3/plugins/config`
(`web_interface/blueprints/api_v3/plugin_config.py`), which validates it against
the schema, writes `config.json` (secret fields go to
`config_secrets.json`) and shows a notification
## Troubleshooting
### Plugin Tab Not Appearing
- Check that the plugin is installed and appears in the **Plugin Manager** tab
- Check browser console for errors
- Reload the page
### Form Not Generating Correctly
- Ensure `config_schema.json` exists in the plugin directory
- Validate your `config_schema.json` against JSON Schema Draft 07
- Check that all properties have a `type` field
- Ensure `default` values match the specified type
- Look for JavaScript errors in browser console
### Configuration Not Saving
- Ensure the plugin is properly installed
- Check that config keys match schema properties
- Verify backend API is accessible
- Check browser network tab for API errors
## Migration Guide
### For Existing Plugins
If your plugin already has a `config_schema.json`:
1. No changes needed! The tab will be automatically generated.
2. Test the generated form to ensure all fields render correctly.
3. Consider adding more descriptive `description` fields.
If your plugin doesn't have a config schema:
1. Create `config_schema.json` based on your current config structure
2. Add descriptions for each property
3. Set appropriate defaults
4. Add validation constraints (min, max, etc.)
### Backward Compatibility
- Plugins without `config_schema.json` still work normally
- Their tab shows plain text, number and checkbox inputs for the keys already
in their `config.json` section, or "No configuration options available for
this plugin." when there are none
- Users can still edit config via the Raw JSON editor
## Beyond the Basic Types
Nested objects (rendered as collapsible sections), `x-widget` widgets such as
`color-picker` and `file-upload`, and more are supported; see
[PLUGIN_CONFIGURATION_GUIDE.md](PLUGIN_CONFIGURATION_GUIDE.md) and
`web_interface/static/v3/js/widgets/README.md`.
## Example Plugins
See these plugins for examples of config schemas:
- `hello-world`: Simple plugin with basic types
- `clock-simple`: Plugin with enum and number types
## Support
For questions or issues:
- Check the main LEDMatrix wiki
- Review plugin documentation
- Open an issue on GitHub
- Join the community Discord