-
Notifications
You must be signed in to change notification settings - Fork 4
plugin sdk events
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.
Plugins have two ways to declare which events they want to receive:
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
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
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();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
SupportedEventSubscriptionwhen 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 overrideSupportedEventSubscription - Use
AddToEventFilter()to extend the current filter without replacing it
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;
}
}| 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 |
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}");
}
}Plugins can also receive actions sent to them or their owned entities.
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;
}
}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}");
}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}"));
});
});
}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; }
}- Platform SDK Events - Subscribing to system events
- Platform SDK Actions - Handling actions
- Plugin SDK Threading - Threading patterns for event handlers
- Plugin SDK Lifecycle - Event handler registration timing
- Plugin SDK Overview - Plugin architecture
-
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
- Overview Introduction to plugin architecture and capabilities.
- Certificates SDK certificate requirements for plugin roles.
- Lifecycle Initialization and disposal patterns.
- Threading Threading model, QueueUpdate, and async patterns.
- State Management Reporting plugin health and diagnostics.
- Configuration Configuration storage and monitoring.
- Restricted Configuration Secure credential storage and admin-only configuration.
- Events Event subscription and handling.
- Queries Query processing and response handling.
- Request Manager Request/response communication with clients.
- Database Database integration and schema management.
- Entity Ownership Understanding plugin-owned entities, running state management, and ownership release.
- Entity Mappings Using EntityMappings for plugin-specific configuration and external system integration.
- Server Management High availability and server failover.
- Custom Privileges Defining and enforcing custom privileges.
- Custom Entity Types Defining and managing plugin-specific entity types.
- Resolving Non-SDK Assemblies Handling third-party dependencies in plugins and workspace modules.
- Deploying Plugins Registering and deploying plugins and workspace modules.
- .NET 8 Support Building plugins with .NET 8 and .NET Standard compatibility.
-
Workspace SDK
- Overview Introduction to client-side UI extensions for Security Desk and Config Tool.
- Certificates SDK certificate requirements for workspace modules.
- Creating Modules Module lifecycle, registration patterns, and assembly resolution.
- Tasks Executable actions, home page entries, and programmatic invocation.
- Pages Page content, lifecycle, descriptors, and navigation.
- Components Dashboard widgets, tiles, maps, credentials, and content builders.
- Tile Extensions Custom tile widgets, views, and properties panels.
- Services Built-in services for dialogs, maps, alarms, badges, and more.
- Contextual Actions Right-click context menu extensions.
- Options Extensions Custom settings pages in application preferences.
- Configuration Pages Entity configuration pages for Config Tool.
- Monitors Multi-monitor support and shared components.
- Shared Components Using monitor and workspace shared UI components.
- Commands Command execution, evaluation, and interception.
- Extending Events Adding custom fields to Security Center events.
- Map Extensions Custom map objects, layers, and providers.
- Timeline Providers Custom timeline event sources for video playback.
- Image Extractors Custom image sources for cardholder photos and custom fields.
- Credential Encoders Encoding credentials with custom encoder components.
- Cardholder Fields Extractors Importing cardholder data from external sources.
- Content Builders Building and customizing tile content in Security Desk.
-
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.
-
- 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 Guide Setup and configuration of the Media Gateway role for video streaming.
- 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.