Skip to content

plugin sdk events

Andre Lafleur edited this page Feb 3, 2026 · 4 revisions

About plugin event handling

Plugins can subscribe to and receive events from Security Center entities. This guide covers plugin-specific event handling patterns.

For general event and action concepts, see Platform SDK Events and Platform SDK Actions.

Event Subscription Methods

Plugins have two ways to declare which events they want to receive:

SupportedEventSubscription Property

Override SupportedEventSubscription to declaratively specify event types at the class level:

public class MyPlugin : Plugin
{
    public override List<EventType> SupportedEventSubscription => new List<EventType>
    {
        EventType.AccessGranted,
        EventType.AccessRefused,
        EventType.DoorOpenedForTooLong,
        EventType.DoorOpenWhileLockSecure
    };

    protected override void OnPluginLoaded()
    {
        Engine.EventReceived += OnEventReceived;
    }

    private void OnEventReceived(object sender, EventReceivedEventArgs e)
    {
        // Handle events
        Logger.TraceDebug($"Event received: {e.EventType} from {e.SourceGuid}");
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Engine.EventReceived -= OnEventReceived;
        }
    }
}

When to use:

  • Event types are known at compile time
  • Event subscription does not change during plugin lifetime
  • Cleaner separation between declaration and handling

Engine.SetEventFilter Method

Use Engine.SetEventFilter() for dynamic event subscription in OnPluginLoaded():

protected override void OnPluginLoaded()
{
    // Set event filter dynamically
    Engine.SetEventFilter(new List<EventType>
    {
        EventType.AccessGranted,
        EventType.AccessRefused,
        EventType.DoorOpenedForTooLong
    });

    Engine.EventReceived += OnEventReceived;
}

When to use:

  • Event types depend on configuration
  • Event subscription changes at runtime
  • Need to add/remove event types dynamically

Event Filter Methods

The Engine provides several methods for managing event filters:

Method Description
SetEventFilter(IEnumerable<EventType>) Sets the event filter, replacing previous filters
AddToEventFilter(EventType) Adds a single event type to the current filter
RemoveFromEventFilter(EventType) Removes a single event type from the current filter
GetEventFilter() Returns the current list of filtered event types
ClearEventFilter() Clears the filter to receive all events
// Add event type based on configuration
if (config.MonitorDoorEvents)
{
    Engine.AddToEventFilter(EventType.DoorOpenedForTooLong);
    Engine.AddToEventFilter(EventType.DoorOpenWhileLockSecure);
}

// Remove event type when no longer needed
Engine.RemoveFromEventFilter(EventType.DoorOpenedForTooLong);

// Get current filter
List<EventType> currentFilter = Engine.GetEventFilter();

SupportedEventSubscription vs SetEventFilter

Understanding when to use each approach:

SupportedEventSubscription (property override):

  • Read by the plugin host at initialization time
  • Sets the initial event filter before OnPluginLoaded() runs
  • Cannot be changed at runtime
  • Returns an empty list by default (no events)
  • Best for static, known event types

SetEventFilter (Engine method):

  • Called at runtime, typically in OnPluginLoaded()
  • Replaces any events set by SupportedEventSubscription
  • Can be modified throughout plugin lifetime
  • Best for dynamic, configuration-driven subscriptions

How they interact:

public class MyPlugin : Plugin
{
    // 1. Plugin host reads this at initialization
    //    Sets initial filter to AccessGranted, AccessRefused
    public override List<EventType> SupportedEventSubscription => new List<EventType>
    {
        EventType.AccessGranted,
        EventType.AccessRefused
    };

    protected override void OnPluginLoaded()
    {
        // 2. This REPLACES the initial filter (not adds to it)
        //    Now only receiving DoorOpenedForTooLong events
        Engine.SetEventFilter(new List<EventType>
        {
            EventType.DoorOpenedForTooLong
        });

        // 3. Use AddToEventFilter to ADD to current filter
        Engine.AddToEventFilter(EventType.DoorOpenWhileLockSecure);
        // Now receiving: DoorOpenedForTooLong, DoorOpenWhileLockSecure

        Engine.EventReceived += OnEventReceived;
    }
}

Recommendations:

  • Use SupportedEventSubscription when event types are fixed and known at compile time
  • Use SetEventFilter() when event types depend on configuration or change at runtime
  • Do not use both unless you intend for SetEventFilter() to override SupportedEventSubscription
  • Use AddToEventFilter() to extend the current filter without replacing it

Receiving Events

EventReceived Handler

Subscribe to Engine.EventReceived to receive events:

protected override void OnPluginLoaded()
{
    Engine.SetEventFilter(new List<EventType>
    {
        EventType.AccessGranted,
        EventType.AccessRefused
    });

    Engine.EventReceived += OnEventReceived;
}

private void OnEventReceived(object sender, EventReceivedEventArgs e)
{
    switch (e.EventType)
    {
        case EventType.AccessGranted:
            HandleAccessGranted(e);
            break;

        case EventType.AccessRefused:
            HandleAccessRefused(e);
            break;
    }
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        Engine.EventReceived -= OnEventReceived;
    }
}

EventReceivedEventArgs Properties

Property Type Description
EventType EventType The type of event received
SourceGuid Guid The entity GUID that generated the event
Timestamp DateTime When the event occurred (UTC)
GroupId Guid Context identifier for grouping related events
Event Event The underlying Event object with additional data

Specialized Event Args

Cast EventReceivedEventArgs to specialized types for specific event data:

private void OnEventReceived(object sender, EventReceivedEventArgs e)
{
    if (e is LicensePlateReadEventArgs lprEvent)
    {
        Logger.TraceDebug($"License plate read: {lprEvent.Context} at {lprEvent.SourceGuid}");
    }
    else if (e is CustomEventReceivedEventArgs customEvent)
    {
        Logger.TraceDebug($"Custom event: {customEvent.CustomEventId}, Message: {customEvent.Message}");
    }
    else if (e is AlarmTriggeredRoutableEventArgs alarmEvent)
    {
        Logger.TraceDebug($"Alarm triggered: {alarmEvent.AlarmEvent.AlarmGuid}");
    }
}

Receiving Actions

Plugins can also receive actions sent to them or their owned entities.

ActionReceived Handler

protected override void OnPluginLoaded()
{
    Engine.ActionReceived += OnActionReceived;
}

private void OnActionReceived(object sender, ActionReceivedEventArgs e)
{
    Logger.TraceDebug($"Action received: {e.ActionType}");

    switch (e.ActionType)
    {
        case ActionType.TriggerAlarm:
            HandleTriggerAlarm(e);
            break;

        case ActionType.Custom:
            if (e.Action is CustomAction customAction)
            {
                HandleCustomAction(customAction);
            }
            break;
    }
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        Engine.ActionReceived -= OnActionReceived;
    }
}

Threading Considerations

Event handlers are called on the engine thread, making it safe to perform Engine operations directly:

private void OnEventReceived(object sender, EventReceivedEventArgs e)
{
    // Safe to access Engine directly - we're on the engine thread
    var entity = Engine.GetEntity(e.SourceGuid);

    Logger.TraceDebug($"Event {e.EventType} from {entity.Name}");
}

Offloading Long-Running Work

For operations that take time (network calls, file I/O), offload to a background thread:

private void OnEventReceived(object sender, EventReceivedEventArgs e)
{
    // Capture event data
    var eventData = new EventData
    {
        EventType = e.EventType,
        SourceGuid = e.SourceGuid,
        Timestamp = e.Timestamp
    };

    // Process on background thread
    Task.Run(async () =>
    {
        // Long-running operation (network, database, etc.)
        await SendToExternalSystemAsync(eventData);

        // Queue entity updates back to engine thread
        Engine.QueueUpdate(() =>
        {
            ModifyPluginState(new PluginStateEntry("LastEvent",
                $"Processed {eventData.EventType} at {DateTime.UtcNow}"));
        });
    });
}

Complete Example

public class AccessMonitorPlugin : Plugin
{
    private readonly ConcurrentQueue<AccessEvent> m_eventQueue = new();
    private CancellationTokenSource m_cancellation;
    private Task m_processingTask;

    // Declaratively specify supported events
    public override List<EventType> SupportedEventSubscription => new List<EventType>
    {
        EventType.AccessGranted,
        EventType.AccessRefused,
        EventType.DoorOpenedForTooLong,
        EventType.DoorOpenWhileLockSecure
    };

    protected override void OnPluginLoaded()
    {
        Engine.EventReceived += OnEventReceived;
        Engine.ActionReceived += OnActionReceived;

        ModifyPluginState(new PluginStateEntry("Status", "Monitoring access events"));
    }

    protected override void OnPluginStart()
    {
        // Start background processor
        m_cancellation = new CancellationTokenSource();
        m_processingTask = Task.Run(() => ProcessEventsAsync(m_cancellation.Token));
    }

    private void OnEventReceived(object sender, EventReceivedEventArgs e)
    {
        // Quick processing on engine thread
        var accessEvent = new AccessEvent
        {
            EventType = e.EventType,
            SourceGuid = e.SourceGuid,
            Timestamp = e.Timestamp
        };

        // Queue for background processing
        m_eventQueue.Enqueue(accessEvent);
    }

    private void OnActionReceived(object sender, ActionReceivedEventArgs e)
    {
        Logger.TraceDebug($"Action received: {e.ActionType}");
    }

    private async Task ProcessEventsAsync(CancellationToken cancel)
    {
        while (!cancel.IsCancellationRequested)
        {
            if (m_eventQueue.TryDequeue(out var accessEvent))
            {
                try
                {
                    await SendToExternalSystemAsync(accessEvent);
                }
                catch (Exception ex)
                {
                    Logger.TraceError(ex, "Failed to process event");
                }
            }
            else
            {
                await Task.Delay(100, cancel);
            }
        }
    }

    private async Task SendToExternalSystemAsync(AccessEvent accessEvent)
    {
        // Send to external system
        await Task.Delay(10); // Simulated network call
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Engine.EventReceived -= OnEventReceived;
            Engine.ActionReceived -= OnActionReceived;

            m_cancellation?.Cancel();
        }
    }
}

public class AccessEvent
{
    public EventType EventType { get; set; }
    public Guid SourceGuid { get; set; }
    public DateTime Timestamp { get; set; }
}

See also

Security Center SDK

  • Security Center SDK Developer Guide Overview of the SDK framework and how to build integrations with Security Center.

    • Platform SDK

      • Overview Introduction to the Platform SDK and core concepts.
      • Connecting to Security Center Step-by-step guide for connecting and authenticating with the SDK.
      • SDK Certificates Details certificates, licensing, and connection validation.
      • Referencing SDK Assemblies Best practices for referencing assemblies and resolving them at runtime.
      • SDK Compatibility Guide Understanding backward compatibility and versioning in the SDK.
      • Entity Guide Explains the core entity model, inheritance, and how to work with entities.
      • Entity Cache Guide Describes the engine's local entity cache and synchronization.
      • Transactions Covers batching operations for performance and consistency.
      • Events Subscribing to real-time system events.
      • Actions Sending actions to Security Center.
      • Security Desk Displaying content on monitors, reading tiles, sending tasks, and messaging operators.
      • Custom Events Defining, raising, and subscribing to custom events.
      • ReportManager Querying entities and activity data from Security Center.
      • ReportManager Query Reference Complete reference of query types, parameters, and response formats.
      • Privileges Checking, querying, and setting user privileges.
      • Partitions Entity organization and access control through partitions.
      • Logging How to configure logging, diagnostics, and debug methods.
    • Plugin SDK

    • Workspace SDK

    • Macro SDK

      • Overview How macros work, creating and configuring macro entities, automation, and monitoring.
      • Developer Guide Developing macro code with the UserMacro class and Security Center SDK.

Web SDK Developer Guide

  • Getting Started Setup, authentication, and basic configuration for the Web SDK.
  • Referencing Entities Entity discovery, search capabilities, and parameter formats.
  • Entity Operations CRUD operations, multi-value fields, and method execution.
  • Partitions Managing partitions, entity membership, and user access control.
  • Custom Fields Creating, reading, writing, and filtering custom entity fields.
  • Custom Card Formats Managing custom credential card format definitions.
  • Actions Control operations for doors, cameras, macros, and notifications.
  • Events and Alarms Real-time event monitoring, alarm monitoring, and custom events.
  • Incidents Incident management, creation, and attachment handling.
  • Reports Activity reports, entity queries, and historical data retrieval.
  • Tasks Listing and executing saved report tasks.
  • Macros Monitoring currently running macros.
  • Custom Entity Types Listing, retrieving, and deleting custom entity type descriptors.
  • System Endpoints License usage, web tokens, and exception handling.
  • Performance Guide Optimization tips and best practices for efficient API usage.
  • Reference Entity GUIDs, EntityType enumeration, and EventType enumeration.
  • Under the Hood Technical architecture, query reflection, and SDK internals.
  • Troubleshooting Common error resolution and debugging techniques.

Media Gateway Developer Guide


Web Player Developer Guide

  • Developer Guide Complete guide to integrating GWP for live and playback video streaming.
  • API Reference Full API documentation with interfaces, methods, properties, and events.
  • Sample Application Comprehensive demo showcasing all GWP features with timeline and PTZ controls.
  • Multiplexing Sample Multi-camera grid demo using a shared WebSocket connection.

Clone this wiki locally