-
Notifications
You must be signed in to change notification settings - Fork 6
plugin sdk state management
Plugins report their health and operational status to Config Tool using ModifyPluginState().
Plugin detects condition
↓
Calls ModifyPluginState()
↓
Plugin Host aggregates states
↓
Config Tool displays in role diagnostics
↓
Role RunningState updated (green/yellow/red)
Key concepts:
- Multiple state contexts can be active simultaneously
- States appear in Config Tool diagnostics
- Error states change role's RunningState to "Not Running" (red)
- Warning states change role's RunningState to "Warning" (yellow)
- States persist until cleared or plugin restarts
The PluginStateEntry class represents a single state condition:
public class PluginStateEntry
{
public PluginStateEntry(string context, string message);
// Properties (set after construction)
public string Context { get; set; }
public string Message { get; set; }
public string Details { get; set; }
public Exception Exception { get; set; }
public bool IsWarning { get; set; }
public bool IsError { get; set; }
public DiagnosticLogMessageParsingMode GuidParsingMode { get; set; }
}Constructor parameters:
-
context- Unique identifier for this state (for example, "Connection", "Database"). Cannot be null or empty. -
message- User-friendly description of the state. Set to empty string to clear the context.
Properties (set after construction):
-
IsWarning- Indicates warning state (yellow) -
IsError- Indicates error state (red). When true,IsWarningis ignored. -
Exception- Optional exception details -
Details- Additional detailed message -
GuidParsingMode- Controls how GUIDs in the message and details are parsed and displayed as clickable entity links in Config Tool diagnostics. Defaults toParseAnyGuid.
The GuidParsingMode property controls how GUIDs embedded in state messages are detected and converted to clickable entity links in Config Tool's diagnostics view.
public enum DiagnosticLogMessageParsingMode
{
ParseAnyGuid, // Default - parses GUIDs with or without brackets
ParseGuidsWithBrackets, // Only parses GUIDs wrapped in curly brackets
NoParsing // No GUID parsing, text displayed as plain text
}ParseAnyGuid (default):
Detects GUIDs in any format, with or without curly brackets. Both formats become clickable entity links.
// GUID becomes a clickable link to the camera entity
ModifyPluginState(new PluginStateEntry("Status", $"Camera {cameraGuid} is offline"));ParseGuidsWithBrackets:
Only detects GUIDs explicitly wrapped in curly brackets {...}. Use this when your message contains text that resembles a GUID but is not an entity reference.
// Only the bracketed GUID becomes a clickable link
// "ABC-12345678-DEF" is not parsed as a GUID
var state = new PluginStateEntry("Status",
$"Camera {{{cameraGuid}}} linked to zone ABC-12345678-DEF")
{
GuidParsingMode = DiagnosticLogMessageParsingMode.ParseGuidsWithBrackets
};
ModifyPluginState(state);NoParsing:
Disables all GUID parsing. All text displays as plain text with no clickable links.
// No GUIDs become clickable
var state = new PluginStateEntry("Debug",
"Transaction ID: 12345678-1234-5678-1234-567812345678")
{
GuidParsingMode = DiagnosticLogMessageParsingMode.NoParsing
};
ModifyPluginState(state);When a GUID is parsed, Config Tool resolves it to the entity name and displays it as a clickable link that navigates to that entity.
Call ModifyPluginState() to report or update the plugin role's state:
protected override void OnPluginLoaded()
{
try
{
InitializeConnection();
ModifyPluginState(new PluginStateEntry("Connection", "Connected to external system"));
}
catch (Exception ex)
{
Logger.TraceError(ex, "Connection failed");
ModifyPluginState(new PluginStateEntry("Connection",
"Failed to connect to external system")
{
Exception = ex,
IsError = true
});
}
}Use ModifyEntityState() to report state for entities owned by your plugin. This allows individual entities created by your plugin to display their own diagnostics in Config Tool.
protected void ModifyEntityState(Guid entityId, PluginStateEntry state);
protected void ModifyEntityState(Guid entityId, PluginStateEntry state, bool isRunningStateSettable);Parameters:
-
entityId- The GUID of the entity to attach the state to -
state- The state entry to report -
isRunningStateSettable- When true (default), the entity'sRunningStateis updated based on the state entry. Set to false to report diagnostics without affecting the entity's running state.
Requirements:
- The entity must be owned by your plugin (
entity.OwnerRole == PluginGuid) - Or the entity must be the plugin role itself
The call fails if you pass a GUID that does not resolve to an entity. If the entity exists but is neither the plugin role nor owned by the plugin, the call has no effect. Reporting an error state (IsError = true) for an entity that cannot enter a Not Running state also fails.
For more information about plugin entity ownership, see About plugin entity ownership.
private void UpdateDeviceStatus(Guid deviceEntityId, bool isOnline)
{
if (isOnline)
{
ModifyEntityState(deviceEntityId,
new PluginStateEntry("Status", "Online"));
}
else
{
ModifyEntityState(deviceEntityId,
new PluginStateEntry("Status", "Offline") { IsWarning = true });
}
}
private void ReportDeviceError(Guid deviceEntityId, Exception ex)
{
ModifyEntityState(deviceEntityId,
new PluginStateEntry("Status", "Communication error")
{
Exception = ex,
IsError = true
});
}Use isRunningStateSettable = false when you want to display diagnostic information without changing the entity's running state indicator:
// Report statistics without affecting the entity's green/yellow/red state
ModifyEntityState(deviceEntityId,
new PluginStateEntry("Statistics", $"Processed {count} events today"),
isRunningStateSettable: false);ModifyPluginState() is a convenience method that calls ModifyEntityState() with the plugin's own GUID:
// These are equivalent:
ModifyPluginState(new PluginStateEntry("Connection", "Connected"));
ModifyEntityState(PluginGuid, new PluginStateEntry("Connection", "Connected"));Indicates normal operation:
ModifyPluginState(new PluginStateEntry("Connection", "Connected"));
ModifyPluginState(new PluginStateEntry("Database", "Database operational"));
ModifyPluginState(new PluginStateEntry("Hardware", "All devices online"));Effect on role:
- Role shows green (Running) if no error/warning states
- State appears in diagnostics as informational
Indicates degraded operation:
ModifyPluginState(new PluginStateEntry("Connection",
"Intermittent connection issues") { IsWarning = true });
ModifyPluginState(new PluginStateEntry("Hardware",
"2 of 10 devices offline") { IsWarning = true });
ModifyPluginState(new PluginStateEntry("Performance",
"Query response time exceeds threshold") { IsWarning = true });Effect on role:
- Role shows yellow (Warning)
- State appears in diagnostics with warning icon
- Plugin continues to operate
When to use:
- Recoverable errors
- Degraded performance
- Partial failures
- Approaching limits
Indicates critical failure:
ModifyPluginState(new PluginStateEntry("Connection",
"Cannot connect to external system") { Exception = ex, IsError = true });
ModifyPluginState(new PluginStateEntry("Database",
"Database connection lost") { Exception = ex, IsError = true });
ModifyPluginState(new PluginStateEntry("License",
"License validation failed") { IsError = true });Effect on role:
- Role shows red (Not Running)
- State appears in diagnostics with error icon
- Plugin may be non-functional
When to use:
- Connection failures
- Database errors
- License/certificate issues
- Critical resource unavailable
Use different context strings for different subsystems:
// Multiple contexts can be active simultaneously
ModifyPluginState(new PluginStateEntry("Connection", "Connected"));
ModifyPluginState(new PluginStateEntry("Database", "Connected"));
ModifyPluginState(new PluginStateEntry("Hardware", "All devices online"));
ModifyPluginState(new PluginStateEntry("License", "Valid"));Common context names:
-
"Connection"- External system connectivity -
"Database"- Database status -
"Hardware"- Hardware device status -
"License"- Licensing status -
"Configuration"- Configuration validation -
"Performance"- Performance metrics -
"Startup"- Initialization status
Context best practices:
- Use descriptive, consistent names
- One context per subsystem
- Clear context scope
- Don't use too many contexts (5-10 max)
Clear a state by setting empty message:
// Report warning
ModifyPluginState(new PluginStateEntry("Connection",
"Slow response") { IsWarning = true });
// Later, when resolved
ModifyPluginState(new PluginStateEntry("Connection", string.Empty));When to clear:
- Condition resolved
- Subsystem recovered
- Error no longer applicable
Important
- Must use same context string to clear
- Empty message clears the state
- Cleared states don't appear in diagnostics
- All states cleared on plugin restart
Report progress during initialization:
protected override void OnPluginLoaded()
{
ModifyPluginState(new PluginStateEntry("Startup", "Initializing..."));
try
{
InitializeConnection();
ModifyPluginState(new PluginStateEntry("Startup", "Connection initialized"));
LoadConfiguration();
ModifyPluginState(new PluginStateEntry("Startup", "Configuration loaded"));
// Clear startup state when complete
ModifyPluginState(new PluginStateEntry("Startup", string.Empty));
// Report operational state
ModifyPluginState(new PluginStateEntry("Status", "Plugin ready"));
}
catch (Exception ex)
{
ModifyPluginState(new PluginStateEntry("Startup",
"Initialization failed") { Exception = ex, IsError = true });
}
}Update states as conditions change:
private void OnConnectionStateChanged(bool connected)
{
if (connected)
{
ModifyPluginState(new PluginStateEntry("Connection", "Connected"));
}
else
{
ModifyPluginState(new PluginStateEntry("Connection",
"Disconnected") { IsWarning = true });
// Attempt reconnection
Task.Run(() => AttemptReconnect());
}
}
private async Task AttemptReconnect()
{
for (int i = 0; i < 3; i++)
{
ModifyPluginState(new PluginStateEntry("Connection",
$"Reconnecting... (attempt {i + 1}/3)") { IsWarning = true });
if (await TryConnectAsync())
{
ModifyPluginState(new PluginStateEntry("Connection", "Reconnected"));
return;
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
ModifyPluginState(new PluginStateEntry("Connection",
"Reconnection failed") { IsError = true });
}The Plugin Host aggregates all plugin states:
Aggregation rules:
- Any error state maps to Role shows red (Not Running).
- Any warning state (no errors) maps to Role shows yellow (Warning).
- All success/cleared states map to Role shows green (Running).
Example:
// Plugin reports multiple states
ModifyPluginState(new PluginStateEntry("Connection", "Connected")); // Green
ModifyPluginState(new PluginStateEntry("Database", "Connected")); // Green
ModifyPluginState(new PluginStateEntry("Hardware",
"1 device offline") { IsWarning = true }); // Yellow
// Role overall state: Yellow (Warning) because of hardware warningPath: System > Roles > [Plugin Role] > Diagnostics
What's displayed:
- Role's aggregated RunningState (green/yellow/red icon)
- List of all active states by context
- State messages
- Timestamps
- Exception details (if provided)
States are NOT:
- Persisted in database
- Available through SDK queries
- Visible to end users (Config Tool only)
private void MonitorConnection()
{
if (IsConnected())
{
ModifyPluginState(new PluginStateEntry("Connection", "Connected"));
}
else if (IsConnecting())
{
ModifyPluginState(new PluginStateEntry("Connection",
"Connecting...") { IsWarning = true });
}
else
{
ModifyPluginState(new PluginStateEntry("Connection",
"Disconnected") { IsError = true });
}
}private void ValidateAndApplyConfiguration(PluginConfig config)
{
if (!ValidateConfiguration(config, out string error))
{
ModifyPluginState(new PluginStateEntry("Configuration",
$"Invalid configuration: {error}") { IsError = true });
return;
}
ModifyPluginState(new PluginStateEntry("Configuration", string.Empty));
ApplyConfiguration(config);
}private void MonitorPerformance()
{
var metrics = GetPerformanceMetrics();
if (metrics.AverageResponseTime > TimeSpan.FromSeconds(5))
{
ModifyPluginState(new PluginStateEntry("Performance",
$"Slow response time: {metrics.AverageResponseTime.TotalSeconds:F2}s")
{ IsWarning = true });
}
else
{
ModifyPluginState(new PluginStateEntry("Performance", string.Empty));
}
}- Plugin SDK overview: Plugin architecture, lifecycle, and components.
- Plugin SDK lifecycle: Plugin initialization, startup, and disposal phases.
- About plugin entity ownership: Plugin-owned entities and their running-state contract.
-
Plugin SDK database: SQL Server integration through
IPluginDatabaseSupport. - About logging: SDK logging configuration and severity levels.
- Overview
- Connecting to Security Center
- SDK certificates
- Referencing SDK assemblies
- SDK compatibility
- Entities
- Entity cache
- Entity loading, cache, and query options
- Transactions
- Events
- Actions
- Security Desk
- Custom events
- ReportManager
- ReportManager query reference
- Access control raw events
- DownloadAllRelatedData and StrictResults
- Privileges
- Partitions
- About custom fields
- About video
- About cameras
- Enrolling a video unit
- Archiver and auxiliary archiver roles
- Archive transfer
- About access control
- About cardholders and credentials
- About doors, areas, elevators, and access points
- About access rules and schedules
- About access control units and interface modules
- Enrolling an access control unit
- Door templates
- Visitors
- Mobile credentials
- About threat levels
- About alarms
- Maps
- Logging
- Overview
- Certificates
- Lifecycle
- Threading
- State management
- Configuration
- Restricted configuration
- Events
- Queries
- Request manager
- Database
- Entity ownership
- Engine extensions
- Entity mappings
- Server management
- Custom privileges
- Custom entity types
- Resolving non-SDK assemblies
- Deploying plugins
- .NET 8 support
- Overview
- Certificates
- Creating modules
- Tasks
- Pages
- Components
- Tile extensions
- Services
- Contextual actions
- Options extensions
- Configuration pages
- Monitors
- Shared components
- Commands
- Extending events
- Map extensions
- Timeline providers
- Image extractors
- Credential encoders
- Credential readers
- Cardholder fields extractors
- Badge printers
- Content builders
- Dashboard widgets
- Incidents
- Logon providers
- Pinnable content builders
- Custom report pages
- Overview
- Getting started
- MediaPlayer
- VideoSourceFilter
- MediaExporter
- MediaFile
- G64 converters
- FileCryptingManager
- PlaybackSequenceQuerier
- PlaybackStreamReader
- OverlayFactory
- PtzCoordinatesManager
- AudioTransmitter
- AudioRecorder
- AnalogMonitorController
- Camera blocking
- Overview
- Getting started
- Referencing entities
- Entity operations
- About access control in the Web SDK
- About video in the Web SDK
- Users and user groups
- Partitions
- Custom fields
- Custom card formats
- Actions
- Events and alarms
- Incidents
- Reports
- Tasks
- Macros
- Custom entity types
- System endpoints
- Performance guide
- Reference
- Under the hood
- Troubleshooting