Skip to content

Getting started

A Mixlar plugin is a folder of Python that plugs into Mixlar Control, the desktop app that drives the Mixlar One (the USB mixer: ESP32-S3, 480×320 touchscreen, 4 faders, 6 macro buttons, a rotary encoder). Plugins subclass MixlarPlugin to add macro actions, a slider mode, and optionally a device widget — a small panel that renders live on the mixer’s screen.

You write against the Mixlar SDK: typed, importable classes that are a faithful port of the same code the app itself uses. Off-app, from mixlar import MixlarPlugin gives you a standalone implementation with autocomplete and type checking. Inside the running app, that same import transparently becomes the app’s own live class — so a plugin authored against the SDK loads completely unmodified. No app install or hardware is required to write, lint, or preview a plugin.

  • Python 3.9 or later
  • pip
  • Mixlar Control, if you want to test on a real (or hot-reloaded) install — optional for everything up through emulation
Terminal window
pip install mixlar-sdk

This installs the mixlar-sdk console script (and the mixlar / mixlar_cli Python packages). You can also run it in place with python -m mixlar_cli, which is handy in CI or before an editable install.

A couple of optional extras add capabilities as you need them:

Extra Adds Needed for
[render] Pillow mixlar-sdk emulate --render (widget PNG previews)
[publish] requests mixlar-sdk publish (nicer HTTP errors; a stdlib fallback works without it)
[all] everything above “just give me all of it”
Terminal window
pip install "mixlar-sdk[all]"

Run create with no arguments for a guided wizard — it asks for the name, id, author, description, and template, then writes the folder and prints next steps:

Terminal window
mixlar-sdk create
┌ Create a Mixlar plugin
◇ Plugin name shown on the plugin card
│ Wave Toggle
◇ Plugin ID lowercase id + folder name
│ wave_toggle
◇ Check if 'wave_toggle' is available on pypi.org? (Y/n)
│ ⠹ Searching…
│ ✓ 'wave_toggle' looks available
◇ Template
│ ● 1 full macros + slider + device widget + settings
│ ○ 2 macro just macro actions
│ …
└ Wave Toggle created 🎉

Prefer to skip the prompts, or script it for CI? Pass a name (and --yes to accept every default) and it scaffolds straight from flags:

Terminal window
mixlar-sdk create "My Plugin" --template full

create derives the folder name from the plugin id, not the display name — "My Plugin" scaffolds into ./my_plugin/. Useful flags:

Flag Does
--template one of macro, slider, widget, full (see below)
--id control the id/slug directly instead of deriving it from the name
--dir scaffold somewhere other than the current directory
--author fill in plugin.json
--force overwrite an existing folder of the same name

Four templates ship in the SDK:

Template Gives you
macro a minimal plugin with one macro action
slider a plugin with a slider mode (SliderMode mixin)
widget a plugin paired with a bundled device widget, no macros
full macros + a slider mode + a bundled widget — the showcase, and the default

Here’s roughly what create --template full scaffolds — a macro action, a settings-backed HTTP call, and a device widget pairing:

from mixlar import MixlarPlugin
from mixlar.macros import MacroActions, action
from mixlar.colors import ACCENT
class MyPlugin(MacroActions, MixlarPlugin):
plugin_id = "my_plugin"
plugin_name = "My Plugin"
plugin_icon_color = ACCENT
#: Pairs this plugin with widgets/my_widget/widget.json.
widget_id = "my_widget"
@action("ping", "Ping", icon="fa5s.satellite-dish")
def _ping(self, step):
self.push_widget_data("status", "pong!") # → WDATA,my_widget.status,pong!
def on_widget_shown(self, widget_id):
self.push_widget_data("status", "idle")
def on_widget_event(self, widget_id, element_id, action):
if element_id == "ping_btn" and action == "press":
self._ping({})

MacroActions derives get_macro_actions(), get_macro_action_groups(), get_macro_action_icons(), and execute_macro_step() from the @action decorators, so there’s no boilerplate quartet of empty methods to override. The full hook reference — lifecycle, macros, slider modes, widgets, settings — lives in Plugin API.

Once you have a folder, the day-to-day cycle is edit → validate → emulate → (eventually) pack:

  1. Lint the package.

    Terminal window
    cd my_plugin
    mixlar-sdk validate

    Checks plugin.json and every bundled widgets/<id>/widget.json for shape and reference errors.

  2. Preview the widget, no hardware required.

    Terminal window
    mixlar-sdk emulate --render preview.png

    Renders the widget exactly as the device would, to a PNG (needs the [render] extra).

  3. Drive it interactively.

    Terminal window
    mixlar-sdk emulate --interactive

    Press buttons, toggle switches, and slide sliders from a REPL against a headless mock device, and inspect the resulting state.

  4. Link it into the app.

    Terminal window
    mixlar-sdk link

    Copies (or symlinks) the package into the app’s plugins directory (%APPDATA%\Mixlar\config\plugins\, overridable with --plugins-dir) so Mixlar Control can load it.

Edit, re-validate, re-emulate, repeat. Once the shape is right, mixlar-sdk link — or the longer-running mixlar-sdk dev, which folds validate + link into a watch loop and re-syncs on every save — puts the package where the app actually loads plugins from.

The app reads plugins from %APPDATA%\Mixlar\config\plugins\<folder>\, each folder being a package with plugin.json at its root. link/dev are just fast ways to keep a copy there in sync with the folder you’re actually editing.

When a plugin is ready to distribute, sign and zip it into a .mixplugin — see Signing and Pack, sign, publish.