workspace sdk custom actions - Genetec/DAP GitHub Wiki
Custom actions add module-defined actions to Security Center's event-to-action system. They appear alongside built-in actions in event-to-action rules, hot actions, scheduled tasks, and other automation contexts. Implementing a custom action requires two parts:
- A server-side plugin that registers the custom action type in the system configuration and handles action execution when triggered
- A Workspace SDK module that provides the configuration user interface through a
CustomActionBuilderandCustomActionView
Both parts must share the same custom action type GUID. Without the server-side plugin, the CustomActionBuilder in the Workspace SDK has no matching descriptor, and IsSupported() returns false. The custom action does not appear in the user interface.
Custom actions are designed for operations that require server-side plugin logic. Use custom actions when you need to:
- Execute plugin-specific logic on the server when an event-to-action rule triggers
- Provide operators with a configurable action that integrates into Security Center's automation rules
- Route actions to a specific plugin role with a custom payload
- The module registers a
CustomActionBuilderwith the workspace duringLoad - When an operator configures an automation rule, the system calls
IsSupported()with aCustomActionContextcontaining the action type and usage context - If supported, the system calls
CreateView()to obtain aCustomActionView. The builder'sCreateView()implementation must callInitialize(Workspace)on the view before returning it - The system sets the view's
Usage,SourceEntityType, andTriggerEventproperties, populatesRecipientsfrom any previously saved configuration, and callsDeserialize()with the previously stored payload string - The system calls
OnInternalInitializationDone()on the view. At this point, all context properties and deserialized state are available - The operator configures the action through the view's interface, setting the
Recipientsand any custom parameters - When the configuration changes, the view calls
OnModified()to notify the system - When the operator saves, the system calls
Serialize()to persist the action's configuration as a payload string - When the configuration dialog closes, the system calls
Dispose()on the view
Important
The Usage, SourceEntityType, TriggerEvent, and Recipients properties are not available during Initialize(). Access them in OnInternalInitializationDone() or later. Calling OnModified() during initialization has no effect because the system suppresses modification notifications until initialization completes.
When the action triggers, the system sends the action to the recipients specified in the view's Recipients collection. The server-side plugin receives the action through the Engine.ActionReceived event. The ActionReceivedEventArgs provides:
| Property | Type | Description |
|---|---|---|
ActionType |
ActionType | The type of action received. Filter for ActionType.CustomAction. |
Action |
Action | The action object. Cast to CustomAction to access custom action properties. |
Timestamp |
DateTime | The action timestamp in UTC. |
The CustomAction object exposes:
| Property | Type | Description |
|---|---|---|
CustomActionType |
Guid | The custom action type identifier. Match this against your descriptor's GUID. |
Payload |
string | The string returned by Serialize() in the view. |
Recipient |
Guid | The entity GUID from the view's Recipients collection. |
SourceEvent |
Event | The event that triggered the action. Contains the event type, source entity, and timestamp. |
Custom actions can also be triggered programmatically through Engine.ActionManager.SendCustomAction(). For more information, see Triggering custom actions programmatically.
The Recipients collection determines which entities receive the action when it triggers. At least one recipient is required for the action configuration to be valid. The recipient is typically the GUID of the plugin role that handles the action. To obtain the plugin role GUID from the workspace, query the entity that the plugin owns and read its OwnerRole property.
Recipients can be any entity GUID, but the system resolves each recipient to the application that owns it for delivery. If a recipient entity does not exist or its owning application is unavailable, that recipient is silently skipped.
Inherit from CustomActionBuilder and implement the abstract members:
public class MyActionBuilder : CustomActionBuilder
{
public override Guid CustomActionType => new Guid("A1B2C3D4-E5F6-7890-ABCD-EF1234567890");
public override CustomActionView CreateView()
{
var view = new MyActionView();
view.Initialize(Workspace);
return view;
}
}| Member | Kind | Description |
|---|---|---|
CustomActionType |
Property (abstract) | Unique identifier for this custom action type. Also used as the component's UniqueId. |
Priority |
Property (virtual) | Display order when multiple custom actions are available. Lower values appear first. Defaults to int.MaxValue. |
IsSupported(CustomActionContext) |
Method (virtual) | Return true when the action is available in the given context. The default implementation queries the system configuration for the custom action type descriptor and returns true if the descriptor exists and its SupportedActionUsage includes the requested usage. |
CreateView() |
Method (abstract) | Create and return a new CustomActionView instance for configuring the action. |
The context passed to IsSupported provides:
| Property | Type | Description |
|---|---|---|
ActionType |
Guid | The custom action's type identifier. |
Usage |
ActionUsage | Where the action is being configured. |
The ActionUsage flags enum indicates where the action is used:
| Value | Description |
|---|---|
None |
No specific context. |
Event2Action |
Event-to-action rules. |
HotAction |
Hot actions triggered by operators. |
ScheduledTasks |
Scheduled task automation. |
ThreatLevel |
Threat level responses. |
FusionRecordAutomationRule |
Fusion record automation rules. |
AreaThreatLevel |
Area threat level responses. |
Automation |
Automation actions. |
All |
All contexts. |
CustomActionView inherits from UserControl. Define the configuration interface in XAML with CustomActionView as the root element:
<customAction:CustomActionView x:Class="MyPlugin.Views.MyActionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customAction="clr-namespace:Genetec.Sdk.Workspace.Components.CustomAction;assembly=Genetec.Sdk.Workspace">
<Grid>
<!-- Configuration controls -->
</Grid>
</customAction:CustomActionView>Implement the code-behind to handle serialization and recipient selection:
public partial class MyActionView : CustomActionView
{
private Guid m_targetId;
public MyActionView()
{
ActionName = "My Custom Action";
ActionDescription = "Performs a custom operation.";
InitializeComponent();
}
public override void Initialize(Workspace workspace)
{
base.Initialize(workspace);
// Set up data sources. Context properties (Usage, SourceEntityType, TriggerEvent)
// are not yet available here.
}
protected override void OnInternalInitializationDone()
{
// All context properties and deserialized state are now available.
// Use Usage, SourceEntityType, TriggerEvent, and Recipients here.
}
protected internal override string Serialize()
{
return m_targetId.ToString();
}
protected internal override void Deserialize(string payload)
{
if (Guid.TryParse(payload, out var id))
{
m_targetId = id;
}
}
private void OnTargetSelected(Guid targetId, Guid pluginRoleId)
{
m_targetId = targetId;
Recipients.Clear();
Recipients.Add(pluginRoleId);
}
}| Member | Kind | Description |
|---|---|---|
ActionName |
Property | Display name for the action in the user interface. |
ActionDescription |
Property | Description text for the action in the user interface. |
IsStateValid |
Property (virtual) | Whether the action's configuration is complete and valid. The system also requires at least one entry in Recipients before the action is saveable. Defaults to true. |
Recipients |
Property | The entity GUIDs that receive the action when it triggers, as an ObservableCollection<Guid>. At least one recipient is required. Typically, add the GUID of the plugin role that handles the action. Modifying this collection automatically calls OnModified(). |
SourceEntityType |
Property | The source entity type that triggers the action. Set by the system after Initialize. |
TriggerEvent |
Property | The event type that triggers the action. Set by the system after Initialize. |
Usage |
Property | The ActionUsage context in which the action is being configured. Set by the system after Initialize. |
Workspace |
Property | The workspace instance. Available after Initialize is called. |
Initialize(Workspace) |
Method (virtual) | Initialize the view with the workspace. Call base.Initialize first. The builder must call this in CreateView() before returning the view. Context properties (Usage, SourceEntityType, TriggerEvent) are not yet available. |
OnInternalInitializationDone() |
Method (protected virtual) | Called after the system sets all context properties and calls Deserialize. Override this to perform setup that depends on Usage, SourceEntityType, TriggerEvent, or deserialized state. |
Serialize() |
Method (protected virtual) | Return a string that represents the action's configuration. This string becomes the CustomAction.Payload that the server-side plugin receives when the action triggers. |
Deserialize(string) |
Method (protected virtual) | Restore the action's configuration from a payload string that was previously returned by Serialize(). Called during initialization before OnInternalInitializationDone(). |
OnModified() |
Method (protected) | Call this when the action's configuration changes to notify the system. The system calls this automatically when Recipients changes. Has no effect during initialization. |
Dispose() |
Method (virtual) | Release resources used by the view. |
The server-side plugin registers the custom action type in the system configuration and handles action execution.
In the plugin's OnPluginLoaded method, create a CustomActionTypeDescriptor and register it with the system configuration. Use AddOrUpdateCustomActionType to register the descriptor. If a descriptor with the same GUID already exists, it is replaced entirely with the new one.
protected override void OnPluginLoaded()
{
var systemConfiguration = Engine.GetEntity<SystemConfiguration>(SystemConfiguration.SystemConfigurationGuid);
var descriptor = systemConfiguration.GetCustomActionTypeDescriptor(CustomActionTypeId);
if (descriptor == null)
{
descriptor = new CustomActionTypeDescriptor(CustomActionTypeId, "My Custom Action")
{
SupportedActionUsage = ActionUsage.All,
Privilege = SdkPrivilege.TriggerAlarm,
};
systemConfiguration.AddOrUpdateCustomActionType(descriptor);
}
}The CustomActionTypeDescriptor defines the action type's metadata:
| Property | Description |
|---|---|
Id |
The unique identifier for the custom action type. |
Name |
The display name for the action type. |
SupportedActionUsage |
The ActionUsage flags that control where the action is available. |
Privilege |
The privilege required to execute the action. Defaults to administrator-only. Set to a specific SdkPrivilege to control which operators can trigger the action. |
HandleByServer |
Whether the server enforces the Privilege configured on the descriptor before the action reaches the recipient. Defaults to true. |
Icon |
The icon displayed for the action in the user interface. |
Description |
Description text for the action type. |
NameKey |
Resource key for the localized action name. Use with ResourceManagerTypeName to localize the display name. |
ResourceManagerTypeName |
Assembly-qualified type name of the ResourceManager that contains localized strings for this action type. The assembly must be loaded on the client machine. If the resource assembly is not available, the system falls back to the Name property. |
Subscribe to Engine.ActionReceived and filter for your custom action type:
Engine.ActionReceived += (sender, e) =>
{
if (e.ActionType == ActionType.CustomAction && e.Action is CustomAction customAction && customAction.CustomActionType == CustomActionTypeId)
{
var payload = customAction.Payload;
var recipient = customAction.Recipient;
var sourceEvent = customAction.SourceEvent;
// Execute the custom action logic
}
};If no handler is subscribed for a custom action type, the action is delivered and discarded without error.
Call RemoveCustomActionType to remove the descriptor from the system configuration. Existing event-to-action rules that reference the removed descriptor are not deleted. Those rules continue to exist but the custom action no longer appears in the configuration user interface.
systemConfiguration.RemoveCustomActionType(CustomActionTypeId);Custom actions can be triggered programmatically through the SDK without event-to-action rules. Call Engine.ActionManager.SendCustomAction() with the descriptor GUID, a list of recipient entity GUIDs, and the payload string:
var recipients = new List<Guid> { pluginRoleGuid };
Engine.ActionManager.SendCustomAction(CustomActionTypeId, recipients, payload);This method validates that the descriptor exists, the current user has the required privilege, at least one recipient is provided, and the payload is not empty. It throws an SdkException if any validation fails.
Note
The CustomActionBuilder component does not require an SDK certificate. However, the server-side plugin that registers and handles the custom action type requires a plugin certificate.
Register the builder in your module's Load method:
public class SampleModule : Module
{
private MyActionBuilder m_actionBuilder;
public override void Load()
{
m_actionBuilder = new MyActionBuilder();
m_actionBuilder.Initialize(Workspace);
Workspace.Components.Register(m_actionBuilder);
}
public override void Unload()
{
if (m_actionBuilder != null)
{
Workspace.Components.Unregister(m_actionBuilder);
}
}
}