fix(web): find installed plugins before anything has discovered them (#594)

The web process discovers plugins lazily: plugin_manifests is empty until
some endpoint calls discover_plugins(). Three routes consulted it without
discovering, so they misbehaved for as long as nothing else had run --
which, after every ledmatrix-web restart, is until someone opens the
dashboard:

- POST /display/on-demand/start answered 404 "Plugin <id> not found"
  (or "Mode <mode> not found"). Measured on a rig: 404 for over three
  minutes after a web restart, until GET /plugins/installed ran. The
  browser UI loads the plugin list first, so API-only callers (the Home
  Assistant MQTT bridge, scripts) are the ones who hit it.
- POST /plugins/toggle answered 404 "Plugin not found".
- POST /config/main did not recognise a plugin section, so it skipped
  secret separation and merged the section as-is: the plugin's API key
  was written to config.json in plain text instead of config_secrets.json.

Add _discovered_plugin_manifests(), which discovers when nothing has been
yet, and rescans once when a specific plugin id (or, for on-demand by
mode, a mode) is not found, so a plugin installed since the last scan is
found too. _installed_plugin_ids() now uses it.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-17 12:38:55 -04:00
committed by GitHub
co-authored by Claude Opus 5
parent f475038895
commit 7e5967e160
5 changed files with 205 additions and 13 deletions
+6 -1
View File
@@ -960,9 +960,14 @@ def save_main_config():
# Any key that matches a plugin ID should be saved as plugin config
# This includes proper secret field handling from schema
plugin_keys_to_remove = []
# Discovered first: a plugin key not recognised here skips secret
# separation below and falls through to the generic merge, which wrote
# the plugin's API key into config.json in plain text whenever nothing
# had yet discovered plugins in this process (any save after a restart).
plugin_manifests = _pkg._discovered_plugin_manifests()
for key in data:
# Check if this key is a plugin ID
if api_v3.plugin_manager and key in api_v3.plugin_manager.plugin_manifests:
if api_v3.plugin_manager and key in plugin_manifests:
plugin_id = key
plugin_config = data[key]