mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
PLUGIN_ARCHITECTURE_SPEC.md
- Added a banner at the top noting this is a historical design doc
written before the plugin system shipped. The doc is ~1900 lines
with 13 stale /api/plugins/* paths (real is /api/v3/plugins/*),
references to web_interface_v2.py (current is app.py), and a
Migration Strategy / Implementation Roadmap that's now history.
Banner points readers at the current docs
(PLUGIN_DEVELOPMENT_GUIDE, PLUGIN_API_REFERENCE,
REST_API_REFERENCE) without needing to retrofit every section.
PLUGIN_CONFIG_ARCHITECTURE.md
- 10 occurrences of /api/plugins/* missing /v3 prefix. Bulk fixed.
DEVELOPER_QUICK_REFERENCE.md
- cache_manager.delete("key") -> cache_manager.clear_cache("key")
with comment noting delete() doesn't exist. Same bug already
documented in PLUGIN_API_REFERENCE.md.
SSH_UNAVAILABLE_AFTER_INSTALL.md
- 4 occurrences of port 5001 -> 5000 in AP-mode and Ethernet/WiFi
recovery instructions.
PLUGIN_CUSTOM_ICONS_FEATURE.md
- Port 5001 -> 5000.
CONFIG_DEBUGGING.md
- Documented /api/v3/config/plugin/<id> and /api/v3/config/validate
endpoints don't exist. Replaced with the real endpoints:
/api/v3/config/main, /api/v3/plugins/schema?plugin_id=,
/api/v3/plugins/config?plugin_id=. Added a note that validation
runs server-side automatically on POST.
STARLARK_APPS_GUIDE.md
- "Plugins -> Starlark Apps" UI navigation path doesn't exist (5
occurrences). Replaced with the real path: Plugin Manager tab,
then the per-plugin Starlark Apps tab in the second nav row.
- "Navigate to Plugins" install step -> Plugin Manager tab.
web_interface/README.md
- Documented several endpoints that don't exist in the api_v3
blueprint:
- GET /api/v3/plugins (list) -> /api/v3/plugins/installed
- GET /api/v3/plugins/<id> -> doesn't exist
- POST /api/v3/plugins/<id>/config -> POST /api/v3/plugins/config
- GET /api/v3/plugins/<id>/enable + /disable -> POST /api/v3/plugins/toggle
- GET /api/v3/store/plugins -> /api/v3/plugins/store/list
- POST /api/v3/store/install/<id> -> POST /api/v3/plugins/install
- POST /api/v3/store/uninstall/<id> -> POST /api/v3/plugins/uninstall
- POST /api/v3/store/update/<id> -> POST /api/v3/plugins/update
- POST /api/v3/display/start/stop/restart -> POST /api/v3/system/action
- GET /api/v3/display/status -> GET /api/v3/system/status
- Also fixed config/secrets.json -> config/config_secrets.json
- Replaced the per-section endpoint duplication with a current real
endpoint list and a pointer to docs/REST_API_REFERENCE.md.
- Documented that SSE stream endpoints are defined directly on the
Flask app at app.py:607-615, not in the api_v3 blueprint.
scripts/install/README.md
- Was missing 3 of the 9 install scripts in the directory:
one-shot-install.sh, configure_wifi_permissions.sh, and
debug_install.sh. Added them with brief descriptions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
432 lines
16 KiB
Markdown
432 lines
16 KiB
Markdown
# Plugin Configuration Tabs - Architecture
|
|
|
|
## System Architecture
|
|
|
|
### Component Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Web Browser │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ Tab Navigation Bar │ │
|
|
│ │ [Overview] [General] ... [Plugins] [Plugin X] [Plugin Y]│ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────┐ ┌──────────────────────────────────┐ │
|
|
│ │ Plugins Tab │ │ Plugin X Configuration Tab │ │
|
|
│ │ │ │ │ │
|
|
│ │ • Install │ │ Form Generated from Schema: │ │
|
|
│ │ • Update │ │ • Boolean → Toggle │ │
|
|
│ │ • Uninstall │ │ • Number → Number Input │ │
|
|
│ │ • Enable │ │ • String → Text Input │ │
|
|
│ │ • [Configure]──────→ • Array → Comma Input │ │
|
|
│ │ │ │ • Enum → Dropdown │ │
|
|
│ └─────────────────┘ │ │ │
|
|
│ │ [Save] [Back] [Reset] │ │
|
|
│ └──────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ HTTP API
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Flask Backend │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ /api/v3/plugins/installed │ │
|
|
│ │ • Discover plugins in plugins/ directory │ │
|
|
│ │ • Load manifest.json for each plugin │ │
|
|
│ │ • Load config_schema.json if exists │ │
|
|
│ │ • Load current config from config.json │ │
|
|
│ │ • Return combined data to frontend │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ /api/v3/plugins/config │ │
|
|
│ │ • Receive key-value pair │ │
|
|
│ │ • Update config.json │ │
|
|
│ │ • Return success/error │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ File System
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ File System │
|
|
│ │
|
|
│ plugins/ │
|
|
│ ├── hello-world/ │
|
|
│ │ ├── manifest.json ───┐ │
|
|
│ │ ├── config_schema.json ─┼─→ Defines UI structure │
|
|
│ │ ├── manager.py │ │
|
|
│ │ └── requirements.txt │ │
|
|
│ └── clock-simple/ │ │
|
|
│ ├── manifest.json │ │
|
|
│ └── config_schema.json ──┘ │
|
|
│ │
|
|
│ config/ │
|
|
│ └── config.json ────────────→ Stores configuration values │
|
|
│ { │
|
|
│ "hello-world": { │
|
|
│ "enabled": true, │
|
|
│ "message": "Hello!", │
|
|
│ ... │
|
|
│ } │
|
|
│ } │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### 1. Page Load Sequence
|
|
|
|
```
|
|
User Opens Web Interface
|
|
│
|
|
▼
|
|
DOMContentLoaded Event
|
|
│
|
|
▼
|
|
refreshPlugins()
|
|
│
|
|
▼
|
|
GET /api/v3/plugins/installed
|
|
│
|
|
├─→ For each plugin directory:
|
|
│ ├─→ Read manifest.json
|
|
│ ├─→ Read config_schema.json (if exists)
|
|
│ └─→ Read config from config.json
|
|
│
|
|
▼
|
|
Return JSON Array:
|
|
[{
|
|
id: "hello-world",
|
|
name: "Hello World",
|
|
config: { enabled: true, message: "Hello!" },
|
|
config_schema_data: {
|
|
properties: {
|
|
enabled: { type: "boolean", ... },
|
|
message: { type: "string", ... }
|
|
}
|
|
}
|
|
}, ...]
|
|
│
|
|
▼
|
|
generatePluginTabs(plugins)
|
|
│
|
|
├─→ For each plugin:
|
|
│ ├─→ Create tab button
|
|
│ ├─→ Create tab content div
|
|
│ └─→ generatePluginConfigForm(plugin)
|
|
│ │
|
|
│ ├─→ Read schema properties
|
|
│ ├─→ Get current config values
|
|
│ └─→ Generate HTML form inputs
|
|
│
|
|
▼
|
|
Tabs Rendered in UI
|
|
```
|
|
|
|
### 2. Configuration Save Sequence
|
|
|
|
```
|
|
User Modifies Form
|
|
│
|
|
▼
|
|
User Clicks "Save"
|
|
│
|
|
▼
|
|
savePluginConfiguration(pluginId)
|
|
│
|
|
├─→ Get form data
|
|
├─→ For each field:
|
|
│ ├─→ Get schema type
|
|
│ ├─→ Convert value to correct type
|
|
│ │ • boolean: checkbox.checked
|
|
│ │ • integer: parseInt()
|
|
│ │ • number: parseFloat()
|
|
│ │ • array: split(',')
|
|
│ │ • string: as-is
|
|
│ │
|
|
│ └─→ POST /api/v3/plugins/config
|
|
│ {
|
|
│ plugin_id: "hello-world",
|
|
│ key: "message",
|
|
│ value: "Hello, World!"
|
|
│ }
|
|
│
|
|
▼
|
|
Backend Updates config.json
|
|
│
|
|
▼
|
|
Return Success
|
|
│
|
|
▼
|
|
Show Notification
|
|
│
|
|
▼
|
|
Refresh Plugins
|
|
```
|
|
|
|
## Class and Function Hierarchy
|
|
|
|
### Frontend (JavaScript)
|
|
|
|
```
|
|
Window Load
|
|
└── DOMContentLoaded
|
|
└── refreshPlugins()
|
|
├── fetch('/api/v3/plugins/installed')
|
|
├── renderInstalledPlugins(plugins)
|
|
└── generatePluginTabs(plugins)
|
|
└── For each plugin:
|
|
├── Create tab button
|
|
├── Create tab content
|
|
└── generatePluginConfigForm(plugin)
|
|
├── Read config_schema_data
|
|
├── Read current config
|
|
└── Generate form HTML
|
|
├── Boolean → Toggle switch
|
|
├── Number → Number input
|
|
├── String → Text input
|
|
├── Array → Comma-separated input
|
|
└── Enum → Select dropdown
|
|
|
|
User Interactions
|
|
├── configurePlugin(pluginId)
|
|
│ └── showTab(`plugin-${pluginId}`)
|
|
│
|
|
├── savePluginConfiguration(pluginId)
|
|
│ ├── Process form data
|
|
│ ├── Convert types per schema
|
|
│ └── For each field:
|
|
│ └── POST /api/v3/plugins/config
|
|
│
|
|
└── resetPluginConfig(pluginId)
|
|
├── Get schema defaults
|
|
└── For each field:
|
|
└── POST /api/v3/plugins/config
|
|
```
|
|
|
|
### Backend (Python)
|
|
|
|
```
|
|
Flask Routes
|
|
├── /api/v3/plugins/installed (GET)
|
|
│ └── api_plugins_installed()
|
|
│ ├── PluginManager.discover_plugins()
|
|
│ ├── For each plugin:
|
|
│ │ ├── PluginManager.get_plugin_info()
|
|
│ │ ├── Load config_schema.json
|
|
│ │ └── Load config from config.json
|
|
│ └── Return JSON response
|
|
│
|
|
└── /api/v3/plugins/config (POST)
|
|
└── api_plugin_config()
|
|
├── Parse request JSON
|
|
├── Load current config
|
|
├── Update config[plugin_id][key] = value
|
|
└── Save config.json
|
|
```
|
|
|
|
## File Structure
|
|
|
|
```
|
|
LEDMatrix/
|
|
│
|
|
├── web_interface_v2.py
|
|
│ └── Flask backend with plugin API endpoints
|
|
│
|
|
├── templates/
|
|
│ └── index_v2.html
|
|
│ └── Frontend with dynamic tab generation
|
|
│
|
|
├── config/
|
|
│ └── config.json
|
|
│ └── Stores all plugin configurations
|
|
│
|
|
├── plugins/
|
|
│ ├── hello-world/
|
|
│ │ ├── manifest.json ← Plugin metadata
|
|
│ │ ├── config_schema.json ← UI schema definition
|
|
│ │ ├── manager.py ← Plugin logic
|
|
│ │ └── requirements.txt
|
|
│ │
|
|
│ └── clock-simple/
|
|
│ ├── manifest.json
|
|
│ ├── config_schema.json
|
|
│ └── manager.py
|
|
│
|
|
└── docs/
|
|
├── PLUGIN_CONFIGURATION_TABS.md ← Full documentation
|
|
├── PLUGIN_CONFIG_TABS_SUMMARY.md ← Implementation summary
|
|
├── PLUGIN_CONFIG_QUICK_START.md ← Quick start guide
|
|
└── PLUGIN_CONFIG_ARCHITECTURE.md ← This file
|
|
```
|
|
|
|
## Key Design Decisions
|
|
|
|
### 1. Dynamic Tab Generation
|
|
|
|
**Why**: Plugins are installed/uninstalled dynamically
|
|
**How**: JavaScript creates/removes tab elements on plugin list refresh
|
|
**Benefit**: No server-side template rendering needed
|
|
|
|
### 2. JSON Schema as Source of Truth
|
|
|
|
**Why**: Standard, well-documented, validation-ready
|
|
**How**: Frontend interprets schema to generate forms
|
|
**Benefit**: Plugin developers use familiar format
|
|
|
|
### 3. Individual Config Updates
|
|
|
|
**Why**: Simplifies backend API
|
|
**How**: Each field saved separately via `/api/v3/plugins/config`
|
|
**Benefit**: Atomic updates, easier error handling
|
|
|
|
### 4. Type Conversion in Frontend
|
|
|
|
**Why**: HTML forms only return strings
|
|
**How**: JavaScript converts based on schema type before sending
|
|
**Benefit**: Backend receives correctly-typed values
|
|
|
|
### 5. No Nested Objects
|
|
|
|
**Why**: Keeps UI simple
|
|
**How**: Only flat property structures supported
|
|
**Benefit**: Easy form generation, clear to users
|
|
|
|
## Extension Points
|
|
|
|
### Adding New Input Types
|
|
|
|
Location: `generatePluginConfigForm()` in `index_v2.html`
|
|
|
|
```javascript
|
|
if (type === 'your-new-type') {
|
|
formHTML += `
|
|
<!-- Your custom input HTML -->
|
|
`;
|
|
}
|
|
```
|
|
|
|
### Custom Validation
|
|
|
|
Location: `savePluginConfiguration()` in `index_v2.html`
|
|
|
|
```javascript
|
|
// Add validation before sending
|
|
if (!validateCustomConstraint(value, propSchema)) {
|
|
throw new Error('Validation failed');
|
|
}
|
|
```
|
|
|
|
### Backend Hook
|
|
|
|
Location: `api_plugin_config()` in `web_interface_v2.py`
|
|
|
|
```python
|
|
# Add custom logic before saving
|
|
if plugin_id == 'special-plugin':
|
|
value = transform_value(value)
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
### Frontend
|
|
|
|
- **Tab Generation**: O(n) where n = number of plugins (typically < 20)
|
|
- **Form Generation**: O(m) where m = number of config properties (typically < 10)
|
|
- **Memory**: Each plugin tab ~5KB HTML
|
|
- **Total Impact**: Negligible for typical use cases
|
|
|
|
### Backend
|
|
|
|
- **Schema Loading**: Cached after first load
|
|
- **Config Updates**: Single file write (atomic)
|
|
- **API Calls**: One per config field on save (sequential)
|
|
- **Optimization**: Could batch updates in single API call
|
|
|
|
## Security Considerations
|
|
|
|
1. **Input Validation**: Schema constraints enforced client-side (UX) and should be enforced server-side
|
|
2. **Path Traversal**: Plugin paths validated against known plugin directory
|
|
3. **XSS**: All user inputs escaped before rendering in HTML
|
|
4. **CSRF**: Flask CSRF tokens should be used in production
|
|
5. **File Permissions**: config.json requires write access
|
|
|
|
## Error Handling
|
|
|
|
### Frontend
|
|
|
|
- Network errors: Show notification, don't crash
|
|
- Schema errors: Graceful fallback to no config tab
|
|
- Type errors: Log to console, continue processing other fields
|
|
|
|
### Backend
|
|
|
|
- Invalid plugin_id: 400 Bad Request
|
|
- Schema not found: Return null, frontend handles gracefully
|
|
- Config save error: 500 Internal Server Error with message
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
|
|
- `generatePluginConfigForm()` for each schema type
|
|
- Type conversion logic in `savePluginConfiguration()`
|
|
- Backend schema loading logic
|
|
|
|
### Integration Tests
|
|
|
|
- Full save flow: form → API → config.json
|
|
- Tab generation from API response
|
|
- Reset to defaults
|
|
|
|
### E2E Tests
|
|
|
|
- Install plugin → verify tab appears
|
|
- Configure plugin → verify config saved
|
|
- Uninstall plugin → verify tab removed
|
|
|
|
## Monitoring
|
|
|
|
### Frontend Metrics
|
|
|
|
- Time to generate tabs
|
|
- Form submission success rate
|
|
- User interactions (configure, save, reset)
|
|
|
|
### Backend Metrics
|
|
|
|
- API response times
|
|
- Config update success rate
|
|
- Schema loading errors
|
|
|
|
### User Feedback
|
|
|
|
- Are users finding the configuration interface?
|
|
- Are validation errors clear?
|
|
- Are default values sensible?
|
|
|
|
## Future Roadmap
|
|
|
|
### Phase 2: Enhanced Validation
|
|
- Real-time validation feedback
|
|
- Custom error messages
|
|
- Dependent field validation
|
|
|
|
### Phase 3: Advanced Inputs
|
|
- Color pickers for RGB arrays
|
|
- File upload for assets
|
|
- Rich text editor for descriptions
|
|
|
|
### Phase 4: Configuration Management
|
|
- Export/import configurations
|
|
- Configuration presets
|
|
- Version history/rollback
|
|
|
|
### Phase 5: Developer Tools
|
|
- Schema editor in web UI
|
|
- Live preview while editing schema
|
|
- Validation tester
|
|
|