Skip to content

Plugin Development

NyxionSoftware edited this page Aug 5, 2026 · 2 revisions

Plugin Development Overview

Simulcast Utility plugins are .NET class libraries containing one public, non-abstract implementation of ISimulcastPlugin. Plugins can observe and control receivers, persist isolated data, expose settings, add themed WPF interface elements, show notifications and dialogs, apply complete application themes, dispatch navigation commands, and log through the host.

The plugin API is still pre-release. Build against the contract assemblies shipped with the exact application version you target.

Lifecycle

Assembly discovered
      |
InitializeAsync (once per loaded plugin instance)
      |
EnableAsync
      |
HandleApplicationArgumentsAsync
      |
DisableAsync
      |
Dispose / unload load context

InitializeAsync

Store the supplied IPluginContext, construct plugin services, and load lightweight state. Do not attach UI, timers, or event handlers here unless they are safe while the plugin is disabled. Initialization can happen again after a plugin-directory or plugin-data-directory reload because a new plugin instance is created.

EnableAsync

Attach event handlers, create timers, apply theme resources, and insert UI. Treat this method as repeatable across disable/enable cycles. Guard against duplicate subscriptions.

DisableAsync

Reverse every action from EnableAsync: unsubscribe events, stop and dispose timers, cancel background work, release media objects, remove themes, and clear references to injected controls. Registered UI cleanup actions and plugin dialogs are also removed by the host, but the plugin remains responsible for its own references.

HandleApplicationArgumentsAsync

Inspect startup arguments intended for the plugin. Ignore arguments you do not own. Avoid blocking startup; offload long work and honor cancellation.

Context services

Member Use
InstallationDirectory Folder containing the plugin assembly and private assets
ReceiverRepository Low-level loading and saving of the full receiver collection
ReceiverManager Source of truth for configured and selected receivers
ReceiverCommandManager Discovery, refresh, status, and receiver command execution
ApplicationDispatcher Request supported navigation/actions from the main shell
ThemeManager Apply/remove a resource dictionary and select light/dark title-bar mode
UiManager Locate named elements, work on the UI thread, add cleanup, show notifications/dialogs
DataStore Plugin-isolated persistent JSON data
Logger Host-created ILogger writing into the application log

See Plugin API Reference for signatures and Receiver APIs for receiver examples.

Logging

The logger category is Plugin.<plugin name>. Messages follow the application's configured minimum level and are written to the same session log.

using Microsoft.Extensions.Logging;

public Task EnableAsync(CancellationToken cancellationToken = default)
{
    _context!.Logger.LogInformation("Enabling schedule integration {Version}.", Info.Version);
    return Task.CompletedTask;
}

private async Task RefreshScheduleAsync(CancellationToken cancellationToken)
{
    try
    {
        _context!.Logger.LogDebug("Refreshing the schedule.");
        await LoadScheduleAsync(cancellationToken);
    }
    catch (Exception ex)
    {
        _context!.Logger.LogError(ex, "Schedule refresh failed.");
        throw;
    }
}

Use structured message templates instead of string interpolation so fields remain searchable. Do not log receiver credentials, private identifiers, access tokens, or complete command payloads containing sensitive values.

Cleanup checklist

  • Unsubscribe every event with the same delegate instance used to subscribe.
  • Cancel and dispose CancellationTokenSource instances.
  • Stop and dispose timers, media players, sockets, watchers, and HTTP responses.
  • Remove plugin resource dictionaries or allow host cleanup to do so.
  • Register removal actions for every injected control.
  • Close plugin-owned windows and dialogs.
  • Clear static references to plugin types and WPF elements.
  • Avoid background tasks that outlive DisableAsync.

Clone this wiki locally