System events
mixlar.events gives your plugin hooks for OS- and app-level events —
a tracked foreground app launching, the M1X connecting over USB, the
system waking from sleep, a mixlar:// deep link being opened.
Delivery model
Section titled “Delivery model”These events are delivered by the host (the desktop app), not
synthesized by the SDK. The app is expected to call broadcast() — or
dispatch() for a single plugin — from its own event sources, passing every
loaded plugin instance and the event name/kwargs.
A plugin author subclasses SystemEventsMixin alongside MixlarPlugin and
overrides the on_* hook(s) it cares about. Everything else stays a
harmless no-op.
from mixlar import MixlarPluginfrom mixlar.events import SystemEventsMixin
class MyPlugin(SystemEventsMixin, MixlarPlugin): def on_device_connect(self): self.push_widget_data("status", "online")
def on_app_launch(self, exe): if exe.lower() == "spotify.exe": self.push_widget_data("mode", "music")Event names and hooks
Section titled “Event names and hooks”| event constant | mixin hook | fires when |
|---|---|---|
APP_LAUNCH |
on_app_launch(exe) |
a tracked foreground app started |
APP_TERMINATE |
on_app_terminate(exe) |
a tracked foreground app exited |
SYSTEM_WAKE |
on_system_wake() |
the OS resumed from sleep |
DEVICE_CONNECT |
on_device_connect() |
the M1X attached over USB serial |
DEVICE_DISCONNECT |
on_device_disconnect() |
the M1X detached / the serial link dropped |
DEEP_LINK |
on_deep_link(url) |
a mixlar://... link was opened (raw URL, unparsed) |
Override only the hooks you need — an unoverridden hook is a no-op, so
mixing in SystemEventsMixin is always safe even if your plugin only cares
about one event.
Dispatch and broadcast
Section titled “Dispatch and broadcast”Two module-level functions drive the hooks:
dispatch(plugin, event, **kwargs)— calls the matching hook on a singleplugininstance, if it has one.broadcast(plugins, event, **kwargs)— callsdispatchfor every plugin in an iterable. This is what the host is expected to call.
Both are crash-isolated: a plugin raising from a hook never takes down the host loop or other plugins, and an unrecognized event name is silently ignored (forward-compatible with future event types added to the SDK).
from mixlar.events import broadcast, APP_LAUNCH
broadcast(loaded_plugins, APP_LAUNCH, exe="spotify.exe")Parsing deep links
Section titled “Parsing deep links”parse_deep_link(url) parses a mixlar://<plugin_id>/<action>?k=v URL into
(plugin_id, params), where params is the query string as a flat dict
with the path’s action segment (if present) stashed under params["action"].
from mixlar.events import parse_deep_link
plugin_id, params = parse_deep_link("mixlar://weather-widget/refresh?city=nyc")# plugin_id == "weather-widget"# params == {"city": "nyc", "action": "refresh"}Malformed input degrades gracefully to ("", {}) rather than raising, so
you can call it on untrusted, OS-supplied URLs without a try/except.
Host integration — where each call belongs
Section titled “Host integration — where each call belongs”None of these call sites exist in PC Software/mixlar_mini.py yet; adding
them is what turns this contract on. This is the map of exactly where, so
whoever wires it up doesn’t have to rediscover it:
-
APP_LAUNCH/APP_TERMINATE— insideForegroundWatcher’s poll loop, wherever it currently diffs the foreground process for auto-switch profiles. Callbroadcast(plugins, APP_LAUNCH, exe=exe_name)when a new foreground exe is first seen, andAPP_TERMINATEwhen a tracked exe disappears. -
SYSTEM_WAKE— hook the Windows sleep/wake notification (e.g. aWM_POWERBROADCAST/PBT_APMRESUMEAUTOMATIChandler, or aQSystemTrayIcon-adjacent OS event) and callbroadcast(plugins, SYSTEM_WAKE)on resume. -
DEVICE_CONNECT/DEVICE_DISCONNECT— in the serial port scanner /CDCReaderconnection lifecycle, where the app already detects the M1X attaching/detaching over USB. Callbroadcast(plugins, DEVICE_CONNECT)/broadcast(plugins, DEVICE_DISCONNECT). -
DEEP_LINK— in themixlar://URL-protocol handler (single-instance activation / custom scheme entry point — this handler doesn’t exist yet either). Parse the incoming URL withparse_deep_link()and callbroadcast(plugins, DEEP_LINK, url=raw_url), or route directly to the matched plugin using the parsedplugin_id.
See the roadmap for the current status of this integration work.
Testing against it today
Section titled “Testing against it today”Since the host doesn’t call these yet, exercise the contract directly in
tests or the emulator by calling dispatch/broadcast yourself:
from mixlar.events import broadcast, APP_LAUNCH
broadcast([my_plugin_instance], APP_LAUNCH, exe="spotify.exe")This is exactly what the app’s ForegroundWatcher will do once wired up —
so a plugin written against SystemEventsMixin today needs no changes when
that lands. You can drive the same calls from the CLI emulator
while iterating on your hooks.
Next steps
Section titled “Next steps”- Settings — persist state your event hooks update.
- Macros — trigger key/text automation from a hook.
- Plugin API — the full
MixlarPluginbase class.