Property Definition¶
Property definitions within adapter-registration.yaml.
Properties can be defined at:
- device type level (
device_types[].properties) - custom function level (
custom_functions[].properties) - bootstrap config (
bootstrap_config) - access profile field level (
access-model.yamlperson_profiles[].fields[])
Structure¶
properties:
property_name:
type: string # required - data type
required: true # optional - is value mandatory?
default: "value" # optional - default value
description: "desc" # optional - property description
# type-specific fields...
Common Fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type |
string | yes | - | Property data type |
required |
bool | no | false |
Marks mandatory property |
default |
any | no | null |
Default value |
description |
string/object | no | "" |
Description (supports multi-language) |
category |
string | no | null |
UI grouping category (overrides function-level category) |
sensitive |
bool | no | false |
Marks sensitive data (passwords, keys) |
private |
bool | no | false |
When true, property is not exposed to UI (internal to adapter) |
Multi-language Description¶
Description supports two formats:
# simple string
description: "Door address on the panel"
# multi-language dictionary
description:
en: "Door address on the panel"
cs: "Adresa dveri na panelu"
When processing, the en key is preferred, otherwise the first available value.
Supported Types¶
Integer Types¶
| Type | C# Type | Range |
|---|---|---|
int |
int |
-2,147,483,648 to 2,147,483,647 |
uint |
uint |
0 to 4,294,967,295 |
long |
long |
-9,223,372,036,854,775,808 to ... |
ulong |
ulong |
0 to 18,446,744,073,709,551,615 |
short |
short |
-32,768 to 32,767 |
ushort |
ushort |
0 to 65,535 |
byte |
byte |
0 to 255 |
sbyte |
sbyte |
-128 to 127 |
Decimal Types¶
| Type | C# Type | Description |
|---|---|---|
float |
float |
single-precision floating point |
double |
double |
double-precision floating point |
decimal |
decimal |
high-precision decimal |
currency |
decimal |
alias for decimal (financial values) |
Other Primitive Types¶
| Type | C# Type | Description |
|---|---|---|
string |
string |
text string |
host |
HostAddress |
DNS hostname, IPv4, or IPv6 address |
ipv4 |
HostAddress |
IPv4 address only |
bool |
bool |
boolean true/false |
guid |
Guid |
UUID identifier |
datetime |
DateTime |
date and time |
datetimeoffset |
DateTimeOffset |
date and time with timezone |
timespan |
TimeSpan |
time interval |
Special Types¶
| Type | Description |
|---|---|
enum |
enumeration type - requires values field |
byte[] |
byte array |
<type>[] |
array of any type (e.g., int[], string[]) |
CustomType |
reference to custom enum defined in adapter |
Device Reference Types¶
When property type matches a type_id defined in the same adapter, it becomes a device reference - a cross-link to another Thing in the device tree.
| Type | C# Type | JSON Value | Description |
|---|---|---|---|
TypeName |
TypeName? |
"guid-string" |
Single device reference (nullable) |
TypeName[] |
IReadOnlyList<TypeName> |
["guid1", "guid2"] |
Array of device references |
Single Device Reference¶
device_types:
- type_id: DoorAlarm
properties:
target_door:
type: Door # matches type_id "Door"
required: true
description: "Door this alarm monitors"
Generated C#:
JSON configuration:
Array Device Reference (Many-to-Many)¶
For relationships where one Thing references multiple others:
device_types:
- type_id: Zone
properties:
partitions:
type: Partition[] # [] suffix for array
required: false
description: "Partitions this zone belongs to"
Generated C#:
internal readonly List<Partition> PartitionsBuilder = [];
public IReadOnlyList<Partition> Partitions => PartitionsBuilder;
JSON configuration:
Resolution¶
Device references are resolved after the entire device tree is built:
- All Things are created with their
device_id - Parent/Children relationships are established
- Device references are resolved by looking up target GUIDs
AddInboundReference()is called for reverse navigation- Deferred validations run (can access resolved references)
Reverse Navigation¶
Every Thing tracks which other Things reference it via ReferencedBy:
// Forward navigation
zone.Partitions // IReadOnlyList<Partition>
// Reverse navigation
partition.ReferencedBy.OfType<Zone>() // all zones referencing this partition
Type-specific Fields¶
For String¶
| Field | Type | Description |
|---|---|---|
max_length |
int | maximum string length |
pattern |
string | regex validation pattern |
properties:
serial_number:
type: string
max_length: 32
pattern: "^[A-Z0-9]+$"
description: "Device serial number"
For Host Addresses¶
Use host for device connection endpoints that may be DNS names or IP literals.
Use ipv4 only when the device or SDK explicitly requires IPv4.
Both host and ipv4 generate Pq.Adapters.Framework.HostAddress properties.
host accepts DNS hostnames, IPv4, and IPv6. ipv4 accepts only dotted IPv4.
For Numeric Types¶
| Field | Type | Description |
|---|---|---|
range |
int[2] | allowed range [min, max] |
properties:
door_address:
type: uint
required: true
range: [1, 32]
description: "Door address (1-32)"
For Enum¶
| Field | Type | Description |
|---|---|---|
values |
string[] | list of allowed values |
properties:
reader_mode:
type: enum
values: ["card_only", "pin_only", "card_and_pin"]
default: "card_only"
description: "Reader authentication mode"
Generated Validation¶
The source generator creates FluentValidation validators based on the definition:
| Definition | Generated Validation |
|---|---|
required: true |
.NotNull() |
required: true + type: string |
.NotNull().NotEmpty() |
max_length: N |
.MaximumLength(N) |
pattern: "regex" |
.Matches(@"regex") |
type: host |
host/IP validation |
type: ipv4 |
IPv4 validation |
range: [min, max] |
.InclusiveBetween(min, max) |
type: enum + values |
.Must(x => values.Contains(x)) |
Access Profile Fields¶
access-model.yaml uses the same property definition shape for
person_profiles[].fields[], with one difference: fields are listed as array items
and include a name field:
person_profiles:
- slot_type: User
fields:
- name: user_level
type: int
required: false
default: 0
range: [0, 3]
category: alarm
description: "Alarm user level"
- name: duress_code
type: string
sensitive: true
pattern: "^[0-9]{4,8}$"
Supported fields are the same where applicable: type, required, default,
range, max_length, pattern, values, category, description, and
sensitive.
Profile fields generate typed profile records used by access synchronization. YAML
defaults become C# property initializers. Required fields are checked by generated
mapping before adapter Transform() executes.
Examples¶
Complex Device Properties¶
device_types:
- type_id: door-controller
name: Door Controller
properties:
address:
type: uint
required: true
range: [1, 64]
description: "Controller address on RS-485 bus"
name:
type: string
max_length: 64
description: "User-defined controller name"
firmware_version:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
description: "Firmware version (semver format)"
unlock_duration:
type: uint
default: 5
range: [1, 60]
description:
en: "Door unlock duration in seconds"
cs: "Doba odemknuti dveri v sekundach"
authentication_mode:
type: enum
values: ["card", "pin", "card_and_pin", "fingerprint"]
default: "card"
description: "Primary authentication method"
Properties with Category and Sensitive¶
device_types:
- type_id: controller
properties:
host:
type: host
required: true
category: connection
description: "Controller host or IP address"
port:
type: uint
default: 4370
category: connection
range: [1, 65535]
description: "TCP port"
password:
type: string
required: true
category: connection
sensitive: true
description: "Device password"
Bootstrap Config Properties¶
bootstrap_config:
connection:
host:
type: host
required: true
description: "Controller hostname or IP address"
port:
type: uint
default: 4370
range: [1, 65535]
description: "TCP port number"
Nullable Behavior¶
Properties are nullable based on type and required combination:
| type | required | C# type |
|---|---|---|
string |
false |
string? |
string |
true |
string |
host |
false |
HostAddress? |
host |
true |
HostAddress |
ipv4 |
false |
HostAddress? |
ipv4 |
true |
HostAddress |
int |
false |
int? |
int |
true |
int |
enum |
false |
string? |
enum |
true |
string |
Naming Conventions¶
- property names in YAML:
snake_case - generated C# properties:
PascalCase
Private Properties¶
Properties marked with private: true are not exposed to UI through DeviceAdapterInformation. They are still generated as C# properties and can be used internally by the adapter.
Use Cases¶
- Technical settings determined by protocol capabilities (e.g.,
use_framework_timers) - Internal configuration that users should not modify
- Protocol-specific parameters
Framework-level Private¶
Define in functions.yaml to make property always private across all adapters:
# functions.yaml
Door:
properties:
use_framework_timers:
type: "bool"
default: true
private: true # always hidden from UI
Adapter-level Override¶
Override framework property to make it private in specific adapter:
# adapter-registration.yaml
device_types:
- type_id: "AccessControlReader"
category: door
functions:
Door:
use_framework_timers:
private: true
default: false # Panel manages timers
Reader:
Adapter overrides can:
- Make a public property private
- Change the default value
- Both
Adapter cannot make a private framework property public (security).
Device Category¶
Each device type must specify a category for UI icon selection.
Required Field¶
Available Categories¶
| Category | Description | Typical Devices |
|---|---|---|
door |
Door access point | ACR, door controller |
reader |
Credential reader | Card reader, biometric |
panel |
Main controller | X1100, X1200 |
module |
Expansion module | X100, X200, X300 |
input |
Input point | Monitor point, sensor |
output |
Output point | Relay, control point |
partition |
Alarm zone | MPG, area |
keypad |
Security keypad | Arming station |
detector |
Intrusion sensor | PIR, contact |
Categories are normalized to lowercase in generated code.
Device Tree Ordering¶
The device tree is sorted for display. By default, siblings under the same
parent are ordered: leaf types before container types (a container is any type
that can have children), then by category, then by the type's address key, then
by natural name. The address key is the type's unique properties (else the
discovery identity, else an address property). Digit runs sort numerically,
so address: 2 comes before address: 10 and Port1 before Port2.
Two optional device_type keys override the defaults.
order (optional, int)¶
Explicit position for this type among its siblings; lower sorts first. When omitted, the position is derived from category and whether the type can contain children.
sort_by (optional, list of property names)¶
Orders instances of this type among same-type siblings, in priority order (natural comparison). Overrides the default address key.