YAML Configuration: Complete Guide¶
Complete guide to adapter-registration.yaml with annotated examples and common patterns.
Table of Contents¶
- Overview
- Top-Level Structure
- Device Types
- Functions
- Properties
- Hierarchies & Parents
- Commands
- Access Model
- Complete Example
Overview¶
adapter-registration.yaml defines:
- Adapter metadata (name, version, manufacturer)
- Device types your hardware supports
- Functions each device type implements
- Properties for configuration
- Parent-child hierarchies
- Supported commands
- Access control model (optional)
Location: Project root, copied to output directory
Schema: adapter-registration.schema.json (for IDE validation)
Top-Level Structure¶
# ==============================================================================
# ADAPTER METADATA
# ==============================================================================
adapter_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" # Unique GUID
name: "My Access Control Adapter" # Display name
version: "1.0.0" # SemVer
manufacturer: "ACME Corp" # Vendor name
description: "Multi-door access control panels" # Brief description
adapter_type: "security" # Category: security | surveillance | intrusion
security_domains: ["access"] # Domains: access | video | intrusion | fire
# ==============================================================================
# CAPABILITIES
# ==============================================================================
capabilities:
auto_discovery: false # true when discovery is supported
time_sync: true # Supports time synchronization
firmware_update: false # Supports firmware updates
config_backup: false # Supports configuration backup/restore
# ==============================================================================
# TRANSPORT
# ==============================================================================
transport:
type: custom # custom | tcp
protocol:
type: "Events" # None | Queue | Events
event_type: "MyAdapter.Communication.DeviceEvent" # C# type for events
address_type: int # int | short | string | guid
Key decisions:
adapter_id- Generate once, never change (devices bind to this)security_domains- Determines UI sections and permissionstransport.type- Matches your communication layeraddress_type- How devices are identified (numeric, string, GUID)
Validate this file against docs/adapter-development/reference/adapter-registration.schema.json. Do not invent block names or property names that the schema does not allow.
Device Types¶
Device types represent physical hardware models. Define one type per hardware variant.
Basic Device Type¶
device_types:
- type_id: "Controller" # Unique within adapter
name: "Access Controller AC-100" # Display name
description: "4-door controller with RS-485 expansion"
category: panel
functions:
ConnectionBase: # Always include (connectivity)
Power: # Power monitoring
Tamper: # Tamper detection
commands:
- pq.command.reset # Supported commands
events:
- pq.event.system.configuration.changed # Events this type reports
properties:
ip_address: # Configuration property
type: host
required: true
category: connection
description: "Controller host or IP address"
Functions: Use framework functions from functions.yaml. See Functions Registry.
Commands: Use framework commands from pq-commands.yaml. See Generated Code API.
Events: List the taxonomy events this type reports on its own — the ones no function
already covers. Each becomes a named publication method on the Thing, so the example above
gives Controller a ConfigurationChanged(timestamp, …) method that your event router
calls. See Declared Event Methods
for how names and parameters are derived.
Device Type with Custom Properties¶
- type_id: "DoorModule"
name: "Door Interface Module"
description: "Single door with reader, strike, REX"
category: door
functions:
Door:
has_lock_sensor: false # Override framework default
has_position_sensor: true
has_rex_sensor: true
doorlongopen_timeout_seconds: 30
Power:
Tamper:
commands:
- pq.command.access.lock
- pq.command.access.unlock
- pq.command.access.open
properties:
# Bus addressing for modular systems
bus_address:
type: int
required: true
range: [1, 32]
category: hardware
description: "RS-485 bus address (1-32)"
# Door-specific configuration
strike_mode:
type: StrikeMode # Custom enum
required: true
default: "FailSecure"
category: door
description: "Strike behavior on power loss"
unlock_duration:
type: int
required: true
default: 5
range: [1, 60]
category: door
description: "Seconds to hold strike unlocked"
Property categories: Group related settings (connection, hardware, door, reader, etc.)
Device Type Inheritance¶
When you have multiple device variants that share most of their implementation, use abstract and extends to avoid code duplication.
device_types:
# Abstract base type - not instantiable
- type_id: "PanelBase"
abstract: true
category: panel
properties:
ip_address:
type: host
required: true
description: "Network module host or IP address"
port:
type: int
default: 10000
functions:
ConnectionBase:
Power:
Tamper:
commands: []
events:
- pq.event.system.configuration.changed
# Concrete variant - inherits from base
- type_id: "Panel48"
extends: "PanelBase"
name: "48-Zone Panel"
description: "48 zones, 4 partitions"
properties:
max_zones:
type: int
default: 48
private: true
# Another variant with different defaults
- type_id: "Panel192"
extends: "PanelBase"
name: "192-Zone Panel"
description: "192 zones, 8 partitions"
properties:
max_zones:
type: int
default: 192
private: true
Key points:
abstract: true- Type cannot be instantiated, serves as baseextends: "BaseTypeId"- Inherits properties, functions, commands from base- Publication methods are generated on the type that declares the events, so
PanelBasegetsConfigurationChangedand both variants inherit it - declare a shared event once on the base - Derived types can add properties or override defaults
- Command handlers on base class work for all derived types
nameis required on concrete (non-abstract) typescategoryis inherited if not specified on derived type
See Things: Device Type Inheritance for details.
Functions¶
Functions provide capabilities to device types. See Functions Registry for complete list of available functions.
Framework Functions¶
Most common:
ConnectionBase- Device connectivity (required for all)Door- Physical door with access controlReader- Credential reader (card, PIN, biometric)Power- Power supply and battery monitoringTamper- Tamper detectionInput- Generic monitored inputOutput- Generic output/relayAccessSynchronization- Credential syncTimeSynchronization- Clock sync
Usage:
functions:
ConnectionBase: # No configuration needed
Door: # With direct property overrides
has_position_sensor: true
doorlongopen_timeout_seconds: 45
TimeSynchronization:
timezone: "EuropePrague" # PqTimeZone enum name, not "Europe/Prague" or Windows timezone ID
sync_interval_minutes: 120
Custom Functions¶
Define adapter-specific functions for non-standard features:
# At top level, after transport
custom_functions:
BiometricEnrollment:
description: "Face/fingerprint enrollment capability"
properties:
max_templates:
type: int
default: 5
range: [1, 10]
description: "Maximum biometric templates per person"
quality_threshold:
type: int
default: 70
range: [1, 100]
description: "Minimum quality score (1-100)"
# Use in device type
device_types:
- type_id: "BiometricReader"
name: "Face Recognition Reader"
category: reader
functions:
ConnectionBase:
Reader:
BiometricEnrollment: # Custom function
max_templates: 3
Custom function events: Generated as adapter-local types, not framework-wide.
Properties¶
Properties configure device behavior. Each property has type, validation, and metadata.
Property Types¶
| Type | Description | Example |
|---|---|---|
string |
Text value | IP address, serial number |
int |
Integer | Port number, timeout seconds |
uint |
Unsigned integer | Bus address, device count |
bool |
Boolean flag | Enable/disable feature |
double |
Decimal number | Battery voltage threshold |
enum |
Custom enumeration | Connection mode, relay mode |
Guid |
Unique identifier | Device ID, person ID |
PqTimeZone |
Timezone | Framework-provided type |
Property Validation¶
properties:
# Hostname, IPv4, or IPv6 address
ip_address:
type: host
required: true
category: connection
description: "Controller host or IP address"
# Integer with range
port:
type: int
required: true
default: 4000
range: [1, 65535]
category: connection
description: "TCP port number"
# Optional with default
timeout_seconds:
type: int
required: false
default: 30
range: [5, 300]
category: communication
description: "Command timeout in seconds"
# Sensitive (masked in UI)
password:
type: string
required: false
sensitive: true
category: connection
description: "Authentication password"
# Custom enum
connection_mode:
type: ConnectionMode # Defined in C# code
required: true
default: "Server"
category: connection
description: "Server = adapter connects, Client = device connects"
Custom Enums¶
Define enums in C# code:
namespace MyAdapter.Definitions;
/// <summary>
/// connection mode
/// </summary>
public enum ConnectionMode
{
/// <summary>
/// adapter connects to device (client mode)
/// </summary>
Server,
/// <summary>
/// device connects to adapter (server mode)
/// </summary>
Client
}
Use in YAML:
Hierarchies & Parents¶
Define parent-child relationships for modular systems.
Simple Hierarchy¶
device_types:
# Parent: Main controller
- type_id: "Controller"
name: "Main Controller"
category: panel
functions:
ConnectionBase:
Power:
# Child: Door module
- type_id: "DoorModule"
name: "Door Module"
category: door
functions:
Door:
parents:
Controller: # Can be child of Controller
max_siblings: 4 # Up to 4 doors per controller
required: true # Must have parent
Result: User creates Controller, then adds up to 4 DoorModule children.
Multi-Level Hierarchy¶
device_types:
# Level 1: Controller
- type_id: "Controller"
name: "System Controller"
category: panel
functions:
ConnectionBase:
Power:
# Level 2: Expansion module
- type_id: "ExpansionModule"
name: "I/O Expansion Module"
category: module
functions:
Power:
Tamper:
properties:
bus_address:
type: int
required: true
range: [1, 32]
parents:
Controller:
max_siblings: 32 # Up to 32 modules per controller
required: true
# Level 3: Door (child of module)
- type_id: "ModuleDoor"
name: "Module Door Port"
category: door
functions:
Door:
properties:
port_number:
type: int
required: true
range: [1, 4]
parents:
ExpansionModule:
max_siblings: 4 # 4 doors per module
required: true
Result: Controller → ExpansionModule → ModuleDoor hierarchy
Optional Parents¶
device_types:
# Standalone OR child device
- type_id: "SmartReader"
name: "Standalone Smart Reader"
category: reader
functions:
ConnectionBase:
Reader:
Door:
parents:
Controller:
max_siblings: 16
required: false # Can exist without parent
Result: Reader works standalone OR as controller child
Commands¶
Declare which PQ commands each device type supports. Framework auto-routes commands to handler methods.
Basic Commands¶
device_types:
- type_id: "Door"
name: "Access Door"
category: door
functions:
Door:
commands:
- pq.command.access.lock # Engage strike
- pq.command.access.unlock # Release strike
- pq.command.access.open # Momentary unlock
- pq.command.access.open.permanent # Hold open
Output Commands¶
- type_id: "RelayOutput"
name: "Generic Relay"
category: output
functions:
Output:
commands:
- pq.command.output.activate
- pq.command.output.deactivate
Emergency Commands¶
- type_id: "Controller"
name: "Security Controller"
category: panel
functions:
ConnectionBase:
commands:
- pq.command.emergency.lockdown # Deny all access
- pq.command.emergency.lockdown.cancel # Restore normal
- pq.command.emergency.evacuation # Unsecure all
- pq.command.emergency.evacuation.cancel
Note: Commands automatically generate handler detection. See Pattern 4: Command Handling.
Access Model¶
Optional. Define access control data model for credential synchronization.
Create access-model.yaml:
model:
# Step 1: schedule entity
- type_id: Schedule
updatable: inplace
properties:
address: uint
periods: SchedulePeriod[]
range: [1, 255]
# Embedded in Schedule
- type_id: SchedulePeriod
properties:
day_mask: uint
start_minute: uint
end_minute: uint
# Step 2: access level references schedules
- type_id: AccessLevel
updatable: inplace
properties:
address: uint
door_schedules: DoorSchedule[]
range: [1, 32000]
# Embedded in AccessLevel; references Schedule by object reference
- type_id: DoorSchedule
properties:
door_address: uint
schedule: Schedule
# Step 3: user references access levels
- type_id: User
properties:
address: uint
name: string
card_number: string
access_levels: AccessLevel[]
range: [1, 10000]
person_profiles:
- slot_type: User
fields:
- name: user_level
type: int
default: 0
range: [0, 3]
category: alarm
description: "Alarm user level"
Dependency order: Declare model entities in dependency order: leaves first,
roots last. Cross-entity references use EntityName or EntityName[]; do not use
raw address integers for model references.
Ownership: You do not declare ownership. The framework infers, from these same
references, which allocator-addressed entities are person-owned roots and which are
shared tables — this drives selective sync. The optional owner: field is an
exceptional override only. See
Pattern 1: Selective Sync Ownership.
Person profiles: Use person_profiles for per-person panel settings that are
not credentials and not grants. Profile fields reuse the same property shape as
device properties where applicable (type, required, default, range,
pattern, category, description, sensitive).
Usage: See Tutorial 3: Access Synchronization.
Complete Example¶
Full adapter-registration.yaml for multi-level access control system:
# ==============================================================================
# Adapter: ACME Access Control System
# Supports AC-1000 controllers with modular expansion
# ==============================================================================
adapter_id: "12345678-1234-5678-1234-567812345678"
name: "ACME Access Control Adapter"
version: "2.1.0"
manufacturer: "ACME Security Systems"
description: "Multi-door access control with RS-485 expansion modules"
adapter_type: "security"
security_domains: ["access"]
capabilities:
auto_discovery: true
time_sync: true
firmware_update: true
config_backup: true
transport:
type: tcp
protocol:
type: "Events"
address_type: int
frame_type: "AcmeFrame"
# ==============================================================================
# Custom Enums (defined in C# code)
# ==============================================================================
# ConnectionMode: Server | Client
# StrikeMode: FailSecure | FailSafe
# ReaderType: Wiegand | OSDP | ClockData
# ==============================================================================
# Device Type Hierarchy:
# AC1000 (controller)
# +-- ExpansionModule (I/O module on RS-485)
# +-- DoorPort (logical door)
# +-- InputPoint (monitored input)
# +-- OutputRelay (control output)
# ==============================================================================
device_types:
# ============================================================================
# AC-1000 - Main Controller
# ============================================================================
- type_id: "AC1000"
name: "ACME AC-1000 Controller"
description: "4-door intelligent controller with RS-485 expansion"
category: panel
functions:
ConnectionBase:
Power:
Tamper:
TimeSynchronization:
timezone: "Utc" # PqTimeZone enum name
sync_interval_minutes: 60
AccessSynchronization:
commands:
- pq.command.reset
- pq.command.emergency.lockdown
- pq.command.emergency.lockdown.cancel
- pq.command.emergency.evacuation
- pq.command.emergency.evacuation.cancel
properties:
# Connection settings
connection_mode:
type: ConnectionMode
required: true
default: "Server"
category: connection
description: "Server = adapter connects, Client = device connects"
ip_address:
type: host
required: true
category: connection
description: "Controller host or IP address"
port:
type: int
required: true
default: 4000
range: [1, 65535]
category: connection
description: "TCP port"
password:
type: string
required: false
sensitive: true
category: connection
description: "Authentication password"
# Hardware identification
serial_number:
type: string
required: true
category: hardware
description: "Device serial number for matching"
# Access control settings
max_cardholders:
type: int
required: false
default: 10000
range: [100, 100000]
category: access
description: "Maximum cardholders in device database"
# ============================================================================
# Expansion Module - RS-485 I/O Module
# ============================================================================
- type_id: "ExpansionModule"
name: "ACME EXP-200 Expansion Module"
description: "RS-485 expansion with 4 doors, 8 inputs, 8 outputs"
category: module
functions:
Power:
Tamper:
properties:
bus_address:
type: int
required: true
range: [1, 32]
category: hardware
description: "RS-485 address (1-32)"
parents:
AC1000:
max_siblings: 32
required: true
# ============================================================================
# Door Port - Logical Door (Reader + Strike + Contact + REX)
# ============================================================================
- type_id: "DoorPort"
name: "Door Interface"
description: "Complete door with reader, strike, contact sensor, REX"
category: door
functions:
Door:
has_lock_sensor: false
has_position_sensor: true
has_rex_sensor: true
fail_safe_mode: false
doorlongopen_timeout_seconds: 30
auto_relock_timeout_seconds: 5
Reader:
commands:
- pq.command.access.lock
- pq.command.access.unlock
- pq.command.access.open
- pq.command.access.open.permanent
properties:
port_number:
type: int
required: true
range: [1, 4]
category: hardware
description: "Door port number on module (1-4)"
reader_type:
type: ReaderType
required: true
default: "Wiegand"
category: reader
description: "Reader interface type"
strike_mode:
type: StrikeMode
required: true
default: "FailSecure"
category: door
description: "Strike behavior on power loss"
unlock_duration:
type: int
required: true
default: 5
range: [1, 60]
category: door
description: "Seconds to hold strike unlocked"
parents:
ExpansionModule:
max_siblings: 4
required: true
AC1000:
max_siblings: 4
required: true
# ============================================================================
# Input Point - Monitored Input
# ============================================================================
- type_id: "InputPoint"
name: "Monitor Point"
description: "Supervised input for alarm sensors"
category: input
functions:
Input:
properties:
input_number:
type: int
required: true
range: [1, 8]
category: hardware
description: "Input number on module (1-8)"
supervision_type:
type: InputSupervision
required: true
default: "NormallyOpen"
category: input
description: "Input supervision (NO/NC/EOL)"
parents:
ExpansionModule:
max_siblings: 8
required: true
# ============================================================================
# Output Relay - Control Output
# ============================================================================
- type_id: "OutputRelay"
name: "Control Relay"
description: "Programmable relay output"
category: output
functions:
Output:
commands:
- pq.command.output.activate
- pq.command.output.deactivate
properties:
output_number:
type: int
required: true
range: [1, 8]
category: hardware
description: "Output number on module (1-8)"
pulse_duration:
type: int
required: false
default: 0
range: [0, 255]
category: output
description: "Pulse duration in 100ms units (0 = latching)"
parents:
ExpansionModule:
max_siblings: 8
required: true
Validation¶
Schema Validation¶
Add schema reference to YAML:
IDE support: VS Code, Rider, Visual Studio recognize schema for autocomplete and validation.
Runtime Validation¶
Framework validates on load:
- Required properties present
- Range constraints respected
- Pattern matches valid
- Enum values recognized
- Parent relationships valid
Errors halt adapter startup - check logs for validation failures.
See Also¶
- Reference:
- YAML Properties - Complete property type reference
- Functions Registry - All framework functions
-
Generated Code API - Using generated types
-
Patterns:
- Access Synchronization - Implementing access model
-
Command Handling - Processing commands
-
Tutorials:
- Hello World - Minimal example
- Event Flow Walkthrough - End-to-end processing
- Access Synchronization - Credential sync guide