Skip to content

Plugin settings

The ui fields on a function are per button: every button carries its own copy. Some options belong to the whole plugin instead — a default location, a unit system, a server address, how often to refresh. Declare those in plugin-settings.json and PyDeck gives your plugin a section under Settings → Plugin settings, stores what the user picks, and hands it to every handler as ctx.settings.

The file is optional. A plugin without it does not appear on the page.


The file

plugin-settings.json sits at the root of the plugin, next to manifest.json:

no.pydeck.weather/
├── manifest.json
├── plugin-settings.json
└── src/…

It is an object with two keys:

Key Type Description
fields array The settings, in the order they are shown. Same field objects as a function's ui array.
save_button boolean false (default): every change saves as the user makes it. true: changes wait for one Save button at the bottom of your plugin's section, which saves them all together. Pick one for the whole plugin.
{
  "save_button": false,
  "fields": [
    {
      "type": "input",
      "id": "default_location",
      "label": "Default location",
      "default": "Oslo",
      "placeholder": "Oslo or 59.91,10.75",
      "description": "Used by any weather button that leaves its own location empty."
    },
    {
      "type": "radio",
      "id": "temperature_unit",
      "label": "Temperature unit",
      "default": "C",
      "options": [
        { "label": "Celsius (°C)", "value": "C" },
        { "label": "Fahrenheit (°F)", "value": "F" }
      ]
    },
    {
      "type": "number",
      "id": "refresh_minutes",
      "label": "Refresh every (minutes)",
      "default": 10,
      "min": 5,
      "max": 120
    }
  ]
}

Use save_button: true when changes are costly to apply one at a time (each one makes a network request, say) or only make sense together, like a host and a port. The page tells the user which kind of section they are in and marks unsaved changes.

Never write to plugin-settings.json

The file is part of your plugin and is replaced on every update. What the user chooses is stored in PyDeck's database, not in the file.

Field types

Every type from UI field types works here, including group, visible_if, api_select and hotkey_recorder. They render the same way as in the button editor. Settings add nothing new, apart from how autosave works:

  • description: one line of help under the field. It works in a button's ui array too.
  • autosave on a single field is ignored on this page. save_button decides for the whole plugin.

Values are checked before they are stored. A number or slider outside min/max, or a select or radio value that is not one of its options, is refused and the error is shown under the field.


Reading settings in a handler

ctx.settings is a dict with one entry per field id (children of a group included). Each entry is the value the user chose, or the field's default if they never changed it:

def on_poll(ctx):
    location = ctx.config.get("location") or ctx.settings.get("default_location", "Oslo")
    unit = ctx.settings.get("temperature_unit", "C")
    ...
  • It is filled on every dispatch (on_load, on_press, on_poll, …) in both the server and the hardware listener. A plugin without the file gets {}.
  • Button and plugin values are never merged. ctx.config stays the button's own values. Your handler decides which one wins, as in the example above.
  • It is a copy. Writing to it changes nothing the user chose. The user changes settings only from the settings page.
  • A default you change in a new version reaches every user who never touched that field. Only values the user actually changed are stored, and Reset to defaults on the settings page forgets them all.
  • Always read with .get() and a fallback. A broken plugin-settings.json gives {} rather than stopping your buttons.

When a setting changes

Your buttons are polled again straight away with _force_refresh set in ctx.config, on every deck. A handler that caches (a forecast, an API response) should rebuild when it sees _force_refresh, so the new setting shows without waiting out the cache. You don't need a separate hook.


Reading settings in an api_<endpoint> function

Functions behind api_select and hotkey_recorder get the settings under config["_settings"], next to the credentials and query parameters. They are kept separate so a setting can never hide a credential with the same name:

def api_entities(config):
    host = config["_settings"].get("host", "localhost")
    ...