Skip to content

plugin sdk engine extensions

Andre Lafleur edited this page Jul 5, 2026 · 1 revision

About plugin engine extensions

A plugin role runs inside the Security Center server, so its Engine can perform operations that an external SDK client cannot. Plugin engine extensions expose those operations as extension methods on the same Engine your plugin role already uses. They cover creating an entity with a GUID your plugin chooses, controlling whether the plugin's root area appears in the area view, encrypting values your plugin stores, and server-side video and Security Center SaaS workflows.

The methods are defined in the Genetec.Sdk.Plugin.Extensions namespace. Import it to call them as extension methods:

using Genetec.Sdk.Plugin.Extensions;

Most of these methods work only from a plugin role, using the plugin engine that Security Center provides to that role. Calling one from an engine that is not a plugin engine, such as a standalone SDK client application connected to Security Center, is not permitted, and the operation does not run. These extensions are for code hosted inside a plugin.

The Engine property on your Plugin class is typed as IEngine. GetEncryptionManager, GetSecurityCenterSaasSettings, and SetSecurityCenterSaasSettingsAsync extend IEngine, so you call them on that property directly. Every other extension here extends Engine, so cast the property to Engine first, as in ((Engine)Engine).CreateCustomEntity(...).

When the base Engine API offers the same operation, use the base API. For example, when you do not need to choose an entity's GUID, use the regular Engine.CreateEntity methods rather than the entity-creation extensions below.

Creating entities with a GUID your plugin chooses

Security Center normally assigns the GUID when an entity is created. The entity-creation extensions let your plugin supply the GUID instead.

This matters for integrations that mirror items from an external system: creating the entity with the GUID your plugin already associates with the external item keeps both systems aligned on a single identifier.

When the external system identifies its items with GUIDs, use the external item's GUID as the entity GUID. Your plugin then needs no mapping table: any part of the integration that holds the external record can address the Security Center entity directly, and when the entity must be created again, for example when your plugin re-synchronizes with the external system, it keeps the same identity.

The value must be a real GUID, one the external system produced with a GUID generator. A value that only has the shape of a GUID because an integer or string identifier was padded, formatted, or hashed into GUID form is not a real GUID; never use one as an entity GUID. When the external system identifies its items with integers or strings instead of GUIDs, these extensions are not for you: create the entity with the regular Engine.CreateEntity methods, let Security Center assign the GUID, and save the assigned GUID with your record of the item when your plugin needs the mapping (see Plugin SDK database).

The extensions mirror the regular creation methods:

  • CreateEntity<TEntity> creates any entity type, with or without a parent.
  • CreateCustomEntity creates a custom entity; use it instead of CreateEntity<TEntity> for custom entities. It rejects Guid.Empty for both the entity GUID and the custom entity type.
  • CreateAccessPoint and CreateDevice create those entity types with their subtype and parent.

Security Center applies the same validation as regular entity creation: the entity type must support creation, the parent must satisfy the type's parent rules, subtypes must be valid, and the connection must have sufficient privileges. Entities created this way are owned by your plugin. See About plugin entity ownership.

using System;
using Genetec.Sdk;
using Genetec.Sdk.Entities;
using Genetec.Sdk.Plugin.Extensions;

private static readonly Guid LockerTypeId =
    new Guid("8BD52821-E2F8-4DE2-A4BD-0F2C5192B9FB");

// entityId is the external system's GUID for this locker, so the
// Security Center entity and the external record share one identity.
private void CreateLocker(Engine engine, string name, Guid entityId)
{
    engine.TransactionManager.ExecuteTransaction(() =>
    {
        CustomEntity locker = engine.CreateCustomEntity(name, LockerTypeId, entityId);
        locker.RunningState = State.Running;
    });
}

Controlling the root area in the area view

The hierarchical entities a plugin creates (areas, doors, cameras, custom entities, and so on) are grouped under a plugin root area. Security Center creates this area automatically when the plugin creates its first hierarchical entity, names it after the plugin role, and shows it in the area view of Config Tool and Security Desk once it holds entities. Operators can browse it like any other area. For more on the root area, see About plugin entity ownership.

Whether the root area should be visible depends on your integration. If operators work with your plugin's entities by browsing the area view, leave it visible. If your plugin's entities exist to support the integration and operators are not meant to browse them there, hide the root area so it does not clutter the area view.

Three extensions manage the area:

  • GetRootArea returns the root area's GUID, or Guid.Empty when the plugin has no root area yet.
  • CreatePluginRootArea creates the root area and returns its GUID. Use it when the area does not exist yet, before your plugin has created any hierarchical entity; it throws if the area already exists.
  • ChangeRootAreaVisibility hides or shows the root area. It requires the area to exist and the visibility to actually change; it is a state change, not an idempotent setter.

Hide the root area after your plugin has added its entities. Creating an entity under the root area makes the area visible again, so hiding it first has no lasting effect.

using System;
using Genetec.Sdk;
using Genetec.Sdk.Plugin.Extensions;

private void HideRootAreaAfterAddingEntities(Engine engine)
{
    if (engine.GetRootArea() == Guid.Empty)
    {
        engine.CreatePluginRootArea();
    }

    engine.ChangeRootAreaVisibility(hideRootAreaFromUI: true);
}

Encrypting values your plugin stores

GetEncryptionManager returns an EncryptionManager that encrypts and decrypts strings. Use it to protect sensitive values your plugin stores, such as credentials for the external system it integrates with, so that what lands in the plugin's configuration or database is ciphertext instead of clear text.

Encryption and decryption work only when dynamic encryption is enabled on the system; the capability is not available on every system.

All plugins on a system encrypt with the same system-managed key. A value encrypted by one plugin can therefore be decrypted by any plugin role on the same system: the encryption protects stored values from readers outside a plugin role, and it does not isolate one plugin's values from another plugin. Do not rely on it to hide a value from other plugins.

EncryptAsync returns the encrypted string. DecryptAsync returns the decrypted string, or the input value unchanged when it cannot decrypt it, so passing a value that was never encrypted returns that value instead of failing.

using System.Threading.Tasks;
using Genetec.Sdk;
using Genetec.Sdk.Plugin.Extensions;
using Genetec.Sdk.Workflows;

private async Task<string> ProtectSecretAsync(IEngine engine, string secret)
{
    EncryptionManager encryption = engine.GetEncryptionManager();
    return await encryption.EncryptAsync(secret);
}

To store credentials and admin-only settings behind built-in access control instead, see About plugin restricted configuration.

Deleting a camera's archived video

The Archiver normally manages the lifecycle of video archives through its retention settings. DeleteExistingArchives is for integrations that must delete specific recordings on demand, for example after the plugin has offloaded them to an external storage system, or when an external process determines that particular footage must be removed.

The method requests deletion of one archive file from the Archiver, Auxiliary Archiver, or Cloud Archive role that manages it. To find the file, query the camera's archive files with VideoFileQuery, which returns each file's path and the role that manages it. The operation is available only when the corresponding feature is enabled on the system.

Two behaviors to plan for:

  • Deleting the camera's last remaining archive file takes two calls; the first call does not remove it.
  • Each deletion is recorded in the activity trails when the system logs that activity type.
using System;
using System.Data;
using System.Threading.Tasks;
using Genetec.Sdk;
using Genetec.Sdk.Plugin.Extensions;
using Genetec.Sdk.Queries;
using Genetec.Sdk.Queries.Video;

// Deletes the camera's archive files in a time range, after your plugin
// has offloaded the recordings to an external system.
private async Task DeleteOffloadedArchivesAsync(
    Engine engine, Guid cameraGuid, DateTime from, DateTime to)
{
    var query = (VideoFileQuery)engine.ReportManager.CreateReportQuery(ReportType.VideoFile);
    query.Cameras.Add(cameraGuid);
    query.TimeRange.SetTimeRange(from, to);
    var results = await Task.Factory.FromAsync(query.BeginQuery, query.EndQuery, null);

    foreach (DataRow row in results.Data.Rows)
    {
        string filePath = row.Field<string>("FilePath");
        Guid archiverGuid = row.Field<Guid>("ArchiveSourceGuid");
        engine.DeleteExistingArchives(filePath, cameraGuid, archiverGuid);
    }
}

Connecting directly to a video unit

Security Center manages the credentials of the video units it controls. GetTemporaryUnitCredentialsAsync exists for device manufacturers that build plugins for their own units: when the plugin must communicate with its device directly, for example to configure a vendor-specific capability that Security Center does not expose, it requests temporary credentials for the unit instead of asking the customer for the unit's password.

The request is restricted by manufacturer. It requires a specific license, and the credentials are issued only for units whose manufacturer matches the manufacturer that license names, so your plugin can obtain credentials for your own devices and not for other vendors' units. The operation must also be enabled on the system. Each request is recorded in the activity trails when the system logs that activity type.

The credentials are temporary and carry their expiry time. Request them once and reuse them until they expire instead of requesting new ones for each operation, and make sure the direct connection does not disrupt the unit's normal operation, such as its video streaming to the Archiver.

using System;
using System.Threading.Tasks;
using Genetec.Sdk;
using Genetec.Sdk.Plugin.Extensions;
using Genetec.Sdk.Requests;

private RetrieveVideoUnitCredentials m_unitCredentials;

// Returns temporary credentials for the unit, requesting new ones only
// when the cached credentials have expired.
private async Task<RetrieveVideoUnitCredentials> GetUnitCredentialsAsync(
    Engine engine, Guid unitGuid)
{
    if (m_unitCredentials == null || m_unitCredentials.ExpiryTime <= DateTime.UtcNow)
    {
        m_unitCredentials = await engine.GetTemporaryUnitCredentialsAsync(unitGuid);
    }

    return m_unitCredentials;
}

Streaming a camera on a user's behalf

GetMediaDelegationToken returns a token that authorizes access to a camera's media as a specific user, without handling that user's credentials. Use it when your plugin supplies media access to another component on a user's behalf: the component presents the token instead of logging on as the user.

The token is issued only for a user that exists and has access to the camera, and it carries a validity period. Check Success on the result, and request a new token when the previous one expires.

using System;
using System.Threading.Tasks;
using Genetec.Sdk;
using Genetec.Sdk.Plugin.Extensions;
using Genetec.Sdk.Workflows.Claims;

// Returns a token that a component can present to access the camera's
// media as the given user.
private async Task<string> GetCameraTokenForUserAsync(
    Engine engine, Guid cameraId, Guid userId)
{
    IAccessTokenResult result = await engine.GetMediaDelegationToken(cameraId, userId);
    return result.Success ? result.TokenAsString : null;
}

Updating the system's Security Center SaaS settings

GetSecurityCenterSaasSettings and SetSecurityCenterSaasSettingsAsync read and update the settings that describe the system's Security Center SaaS connection: the system identifier and name, the web, mobile, and authentication role identifiers, the SaaS web application URL, and the cloud endpoints. They exist for plugins that manage a system's SaaS connection; other integrations have no reason to change these settings.

Reading and updating the settings requires an administrator user, and updating also requires the plugin engine.

using System.Threading.Tasks;
using Genetec.Sdk;
using Genetec.Sdk.Entities.SecurityCenterSaas;
using Genetec.Sdk.Plugin.Extensions;

// Updates the SaaS web application URL in the system's settings.
private async Task UpdateSaasWebAppUrlAsync(IEngine engine, string webAppUrl)
{
    SecurityCenterSaasSettings settings = engine.GetSecurityCenterSaasSettings();
    settings.SaasWebAppUrl = webAppUrl;
    await engine.SetSecurityCenterSaasSettingsAsync(settings);
}

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Synergis RIO

Media Gateway

Genetec Web Player

Clone this wiki locally