Skip to content

plugin sdk configuration

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

About plugin configuration

Plugins store configuration in the Role.SpecificConfiguration property as a serialized string (typically JSON).

Configuration Architecture

PluginDescriptor.SpecificDefaultConfig
    ↓
Provides default configuration when role is created
    ↓
Role.SpecificConfiguration
    ↓
Stored in Security Center database
    ↓
Plugin reads on startup and monitors for changes

Key points:

  • Configuration is stored per-role instance
  • Multiple instances of same plugin have separate configurations
  • Changes persist across plugin restarts
  • Configuration changes trigger EntitiesInvalidated event
  • Configuration is a string, plugin chooses serialization format

Default Configuration

Define defaults in PluginDescriptor.SpecificDefaultConfig:

public class MyPluginDescriptor : PluginDescriptor
{
    public override string SpecificDefaultConfig
    {
        get
        {
            var config = new PluginConfig
            {
                ServerUrl = "http://localhost:8080",
                PollInterval = 30,
                EnableLogging = true,
                Timeout = TimeSpan.FromSeconds(10)
            };
            return JsonConvert.SerializeObject(config);
        }
    }
}

When applied:

  • Applied when plugin role is first created in Config Tool
  • Not applied to existing roles (preserves customization)
  • If SpecificDefaultConfig returns empty string, no default applied

Serialization format:

  • JSON is recommended (human-readable, well-supported)
  • XML is also supported
  • Binary formats work but harder to troubleshoot
  • Must be string-serializable

Reading Configuration

Read configuration in OnPluginLoaded():

public class PluginConfig
{
    public string ServerUrl { get; set; }
    public int PollInterval { get; set; }
    public bool EnableLogging { get; set; }
    public TimeSpan Timeout { get; set; }
}

protected override void OnPluginLoaded()
{
    var config = LoadConfiguration();
    ApplyConfiguration(config);
}

private PluginConfig LoadConfiguration()
{
    var role = Engine.GetEntity<Role>(PluginGuid);
    string configJson = role.SpecificConfiguration;
    
    if (string.IsNullOrEmpty(configJson))
    {
        Logger.TraceWarning("No configuration found, using defaults");
        return CreateDefaultConfiguration();
    }
    
    try
    {
        return JsonConvert.DeserializeObject<PluginConfig>(configJson);
    }
    catch (Exception ex)
    {
        Logger.TraceError(ex, "Failed to parse configuration, using defaults");
        return CreateDefaultConfiguration();
    }
}

private void ApplyConfiguration(PluginConfig config)
{
    Logger.TraceInformation($"Applying configuration: ServerUrl={config.ServerUrl}");
    
    m_serverUrl = config.ServerUrl;
    m_pollInterval = config.PollInterval;
    m_enableLogging = config.EnableLogging;
    m_timeout = config.Timeout;
}

Best practices:

  • Always handle missing/invalid configuration gracefully
  • Log configuration values (except secrets)
  • Validate configuration values
  • Fall back to safe defaults on errors

Updating Configuration

Plugins can programmatically update their configuration:

private void UpdateConfiguration(PluginConfig config)
{
    Engine.TransactionManager.ExecuteTransaction(() =>
    {
        var role = Engine.GetEntity<Role>(PluginGuid);
        role.SpecificConfiguration = JsonConvert.SerializeObject(config);
    });
    
    Logger.TraceInformation("Configuration updated");
}

When to update:

  • In response to request from config page
  • After auto-discovery of settings
  • To persist runtime state changes
  • When defaults need adjustment

Considerations:

  • Updates trigger EntitiesInvalidated event
  • Other plugin instances will see the change
  • Changes are immediately visible in Config Tool
  • Use transactions to ensure atomicity

Monitoring Configuration Changes

Subscribe to EntitiesInvalidated to detect configuration changes:

protected override void OnPluginLoaded()
{
    Engine.EntitiesInvalidated += OnEntitiesInvalidated;
    
    // Load initial configuration
    var config = LoadConfiguration();
    ApplyConfiguration(config);
}

private void OnEntitiesInvalidated(object sender, EntitiesInvalidatedEventArgs e)
{
    // Check if our role was updated
    var roleUpdate = e.Entities.FirstOrDefault(u => u.EntityGuid == PluginGuid);
    
    if (roleUpdate != null)
    {
        Logger.TraceDebug("Plugin configuration changed");
        
        // Reload and apply configuration
        var config = LoadConfiguration();
        ApplyConfiguration(config);
    }
}

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

When fired:

  • Configuration changed in Config Tool
  • Configuration updated programmatically by SDK client
  • Any other role property changed
  • Occurs on the engine thread (safe to use Engine)

Configuration Validation

Validate configuration values:

private PluginConfig LoadConfiguration()
{
    var config = ParseConfiguration();
    
    if (!ValidateConfiguration(config, out string error))
    {
        Logger.TraceError($"Invalid configuration: {error}");
        ModifyPluginState(new PluginStateEntry("Configuration",
            $"Invalid configuration: {error}") { IsError = true });
        return CreateDefaultConfiguration();
    }
    
    return config;
}

private bool ValidateConfiguration(PluginConfig config, out string error)
{
    if (string.IsNullOrWhiteSpace(config.ServerUrl))
    {
        error = "ServerUrl is required";
        return false;
    }
    
    if (!Uri.IsWellFormedUriString(config.ServerUrl, UriKind.Absolute))
    {
        error = "ServerUrl is not a valid URL";
        return false;
    }
    
    if (config.PollInterval < 1 || config.PollInterval > 3600)
    {
        error = "PollInterval must be between 1 and 3600 seconds";
        return false;
    }
    
    if (config.Timeout < TimeSpan.FromSeconds(1))
    {
        error = "Timeout must be at least 1 second";
        return false;
    }
    
    error = null;
    return true;
}

Validation strategies:

  • Validate on load and on change
  • Report errors via ModifyPluginState()
  • Fall back to safe defaults on validation failure
  • Log validation errors
  • Don't crash plugin on invalid config

Secrets and Sensitive Data

DO NOT store passwords or API keys in SpecificConfiguration - it is stored as plaintext in the database.

For sensitive data, use RestrictedConfiguration instead:

// ❌ WRONG - Plaintext password in SpecificConfiguration
public class PluginConfig
{
    public string ServerUrl { get; set; }
    public string Password { get; set; }  // DON'T DO THIS!
}

// ✅ CORRECT - Use RestrictedConfiguration for credentials
protected override void OnPluginLoaded()
{
    // Load non-sensitive config from SpecificConfiguration
    var config = LoadConfiguration();
    
    // Load credentials from RestrictedConfiguration
    var role = Engine.GetEntity<Role>(PluginGuid);
    if (role.RestrictedConfiguration.TryGetPrivateValue("Password", out var securePassword))
    {
        // Use secure password
        InitializeConnection(config.ServerUrl, securePassword);
        securePassword.Dispose();
    }
}

See Plugin SDK Restricted Configuration for secure credential storage.

Configuration Storage Comparison

Storage Purpose Format Security Access
SpecificConfiguration General settings JSON/XML/any string Plaintext Anyone with role access
RestrictedConfiguration.AdminConfigXml Admin config XML Plaintext Plugin or administrators
RestrictedConfiguration (methods) Credentials SecureString Secure Plugin only (read), admin (write)

Best practice: Store non-sensitive configuration in SpecificConfiguration, sensitive credentials in RestrictedConfiguration using GetPrivateValue()/SetPrivateValue() methods.

Configuration Migration

Handle configuration schema changes between versions:

private PluginConfig LoadConfiguration()
{
    var role = Engine.GetEntity<Role>(PluginGuid);
    string configJson = role.SpecificConfiguration;
    
    if (string.IsNullOrEmpty(configJson))
        return CreateDefaultConfiguration();
    
    try
    {
        // Try to parse as current version
        var config = JsonConvert.DeserializeObject<PluginConfig>(configJson);
        
        // Check if migration needed
        if (NeedsMigration(config))
        {
            config = MigrateConfiguration(config);
            
            // Save migrated configuration
            Engine.TransactionManager.ExecuteTransaction(() =>
            {
                role.SpecificConfiguration = JsonConvert.SerializeObject(config);
            });
            
            Logger.TraceInformation("Configuration migrated to new format");
        }
        
        return config;
    }
    catch (Exception ex)
    {
        Logger.TraceError(ex, "Configuration parse error");
        return CreateDefaultConfiguration();
    }
}

private bool NeedsMigration(PluginConfig config)
{
    // Check for old property or schema version
    return config.SchemaVersion < 2;
}

private PluginConfig MigrateConfiguration(PluginConfig config)
{
    if (config.SchemaVersion == 1)
    {
        // Migrate v1 to v2
        config.NewProperty = ConvertOldProperty(config.OldProperty);
        config.SchemaVersion = 2;
    }
    
    return config;
}

Migration best practices:

  • Include schema version in configuration
  • Support migrating from any previous version
  • Log migrations

Configuration UI

Custom config pages can read/write configuration via RequestManager:

Plugin side:

protected override void OnPluginLoaded()
{
    // Use Plugin's protected helper methods to register handlers
    AddRequestHandler<GetConfigRequest, PluginConfig>(HandleGetConfig);
    AddRequestHandler<SetConfigRequest, SetConfigResponse>(HandleSetConfig);
}

private PluginConfig HandleGetConfig(GetConfigRequest request)
{
    return LoadConfiguration();
}

private SetConfigResponse HandleSetConfig(SetConfigRequest request)
{
    try
    {
        if (!ValidateConfiguration(request.Config, out string error))
        {
            return new SetConfigResponse { Success = false, Error = error };
        }
        
        UpdateConfiguration(request.Config);
        ApplyConfiguration(request.Config);
        
        return new SetConfigResponse { Success = true };
    }
    catch (Exception ex)
    {
        Logger.TraceError(ex, "Failed to update configuration");
        return new SetConfigResponse 
        { 
            Success = false, 
            Error = ex.Message 
        };
    }
}

See Plugin SDK Request Manager for request/response details.

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