Skip to content

Configuration Elements

Configuration elements are the building blocks of a Schema. Each element defines a typed field with a display name, optional default, description, and validation constraints. The Doover UI renders each element type as an appropriate form control.

Common Parameters

All element types inherit these parameters from the base ConfigElement class.

ParameterTypeDefaultDescription
display_namestr(required)The label shown in the UI form
defaultvariesNotSetDefault value. If NotSet, the element is required.
descriptionstr | NoneNoneHelp text shown below the form field
hiddenboolFalseWhether to hide this element from the UI
namestr | NoneNoneOverride the auto-generated internal key
positionint | NoneNoneOverride the auto-assigned UI ordering position
advancedbool | NoneNoneWhether this element appears in an "advanced" section
requiredbool | NoneNoneOverride the default required-ness (derived from default)
deprecatedbool | NoneNoneMark the element as deprecated
formatstr | NoneNoneJSON Schema format hint for specialised rendering

Primitive Types

Integer

An integer field. Renders as a number input in the UI.

from pydoover.config import Integer

# Basic integer with no constraints
count = Integer("Sensor Count", default=4)

# Integer with validation constraints
pin_number = Integer(
    "GPIO Pin",
    default=0,
    minimum=0,
    maximum=27,
    description="BCM pin number on the Raspberry Pi",
)

# Integer that must be a multiple of a specific value
step_count = Integer(
    "Step Count",
    default=100,
    multiple_of=10,
    description="Must be a multiple of 10",
)
ParameterTypeDescription
minimumint | NoneMinimum value (inclusive)
exclusive_minimumint | NoneMinimum value (exclusive)
maximumint | NoneMaximum value (inclusive)
exclusive_maximumint | NoneMaximum value (exclusive)
multiple_ofint | NoneValue must be a multiple of this

Number

A floating-point number field. Inherits all validation parameters from Integer but uses JSON type "number" and Python type float.

from pydoover.config import Number

temperature_threshold = Number(
    "Temperature Threshold",
    default=40.0,
    minimum=0.0,
    maximum=100.0,
    description="Alert when temperature exceeds this value (Celsius)",
)

Boolean

A true/false field. Renders as a toggle or checkbox in the UI.

from pydoover.config import Boolean

alerts_enabled = Boolean(
    "Enable Alerts",
    default=True,
    description="Whether to send temperature alerts",
)

String

A text field with optional length and pattern constraints.

from pydoover.config import String

site_name = String(
    "Site Name",
    default="Unnamed Site",
    description="Display name for this installation",
)

# String with a regex pattern constraint
serial = String(
    "Serial Number",
    pattern=r"^[A-Z]{3}-\d{6}$",
    description="Format: AAA-000000",
)

# String with a maximum length
notes = String(
    "Notes",
    default="",
    length=500,
)
ParameterTypeDescription
lengthint | NoneMaximum string length
patternstr | NoneRegex pattern the string must match

DateTime

A date-time field. Internally a String with format="date-time". The UI renders a date-time picker.

from pydoover.config import DateTime

start_time = DateTime(
    "Start Time",
    default="2026-01-01T00:00:00Z",
    description="When to begin data collection",
)

Complex Types

Enum

A selection from a fixed list of choices. Renders as a dropdown in the UI.

Choices can be a list of strings or floats, or a Python Enum type.

from pydoover.config import Enum

# String choices
unit = Enum(
    "Temperature Unit",
    choices=["Celsius", "Fahrenheit", "Kelvin"],
    default="Celsius",
)

Using a Python Enum type maps the stored string value back to the Enum member when accessed.

import enum
from pydoover.config import Enum as ConfigEnum

class EngineType(enum.Enum):
    honda = "Honda"
    john_deere = "John Deere"
    cat = "Caterpillar"

engine = ConfigEnum(
    "Engine Type",
    choices=EngineType,
    default=EngineType.honda,
)

When using a Python Enum, accessing engine.value returns the Enum member (e.g., EngineType.honda), not the string. This lets you use Enum-specific attributes and pattern matching.

Enum values can also be objects, provided they implement __str__. Each __str__ return value must be unique across the choices.

import enum

class Priority:
    def __init__(self, name, level):
        self.name = name
        self.level = level

    def __str__(self):
        return self.name

class PriorityType(enum.Enum):
    low = Priority("Low", 1)
    medium = Priority("Medium", 2)
    high = Priority("High", 3)

priority = ConfigEnum(
    "Alert Priority",
    choices=PriorityType,
    default=PriorityType.low,
)
# priority.value.level == 1
Information Circle
Example

A monitoring processor uses Python enums for both hardware version and UI layout mode, allowing the config form to present readable options while the processor uses enum member comparisons:

import enum
from pydoover import config

class DeviceVersion(enum.Enum):
    V1 = "V1"
    V2 = "V2"

class DisplayMode(enum.Enum):
    SUBMODULE = "Submodule"
    TAB = "Tab"

class MonitorConfig(config.Schema):
    version = config.Enum(
        "Version",
        choices=DeviceVersion,
        default=DeviceVersion.V2,
    )
    display_mode = config.Enum(
        "Display Mode",
        choices=DisplayMode,
        default=DisplayMode.SUBMODULE,
        description="Render each sensor as a collapsible submodule, or as a tab.",
    )

Array

A list of homogeneously-typed elements. The element parameter defines the type of each item in the array.

from pydoover.config import Array, String, Integer

# Array of strings
allowed_channels = Array(
    "Allowed Channels",
    element=String("Channel Name"),
    default=[],
    description="Channel names this processor is allowed to write to",
)

# Array with min/max constraints
sensor_ids = Array(
    "Sensor IDs",
    element=Integer("Sensor ID"),
    min_items=1,
    max_items=16,
    unique_items=True,
    description="IDs of the sensors to monitor (1 to 16, unique)",
)
ParameterTypeDescription
elementConfigElementThe element type for each item in the array
min_itemsint | NoneMinimum number of items
max_itemsint | NoneMaximum number of items
unique_itemsbool | NoneWhether items must be unique

After config is loaded, access the array elements via the .value property (which returns a list of ConfigElement instances) or the .elements property.

for item in self.config.allowed_channels.elements:
    print(item.value)
Information Circle
Example

A monitoring processor uses an Array of custom Object elements to map sensor IDs to display labels. Each Object has typed fields that the operator configures per deployment:

from pydoover import config

class SensorLabel(config.Object):
    id = config.Integer(
        "Sensor ID",
        description="Numeric ID reported by the device.",
    )
    name = config.String(
        "Sensor Name",
        description="Display label, e.g. 'Top Section'.",
    )
    show_on_overview = config.Boolean("Show on Overview", default=False)

class MonitorConfig(config.Schema):
    sensor_labels = config.Array(
        "Sensor Labels",
        element=SensorLabel("Sensor"),
        default=[],
        description="Mapping of sensor ID to display label",
    )

At runtime, iterate the array to build dynamic UI elements:

for elem in self.config.sensor_labels.elements:
    s_id = elem.id.value
    label = elem.name.value
    self.add_element(build_sensor_submodule(s_id, label, ...))

Object

A nested group of elements. Renders as a collapsible section in the UI form.

Elements can be declared as class attributes on an Object subclass, or added dynamically.

from pydoover.config import Object, Number, Boolean

class AlertSettings(Object):
    high_threshold = Number("High Threshold", default=40.0)
    low_threshold = Number("Low Threshold", default=5.0)
    enabled = Boolean("Enabled", default=True)

Then use the subclass in your schema.

from pydoover.config import Schema

class Config(Schema):
    alerts = AlertSettings("Alert Settings", description="Temperature alert config")

Access nested values by chaining attribute access.

high = self.config.alerts.high_threshold.value
enabled = self.config.alerts.enabled.value
ParameterTypeDescription
additional_elementsboolWhether to accept keys not declared in the schema. Default: True.
collapsibleboolWhether the UI section is collapsible. Default: True.
default_collapsedboolWhether the section starts collapsed. Default: False.

For advanced Object features (inheritance, Django-style overrides, deep nesting), see Advanced Configuration.

Doover-Specific Types

These element types are specialised for Doover platform resources. They render as resource pickers in the UI.

Application

A reference to a Doover application (by resource ID). Renders as a dropdown of available applications.

from pydoover.config import Application

target_app = Application(
    "Target Application",
    description="The application to forward data to",
)

ApplicationInstall

A reference to an installed application instance. Renders as a dropdown of installed applications.

from pydoover.config import ApplicationInstall

source_app = ApplicationInstall(
    "Source Application",
    description="The installed application to read data from",
)

Device

A device reference. Renders as a device picker. Internally a String with pattern=r"\d+" and format "doover-resource-device".

from pydoover.config import Device

target_device = Device(
    "Target Device",
    description="The device to send commands to",
)

Group

A group reference. Similar to Device but for device groups.

from pydoover.config import Group

monitoring_group = Group(
    "Monitoring Group",
    description="The group of devices to monitor",
)

DevicesConfig and GroupsConfig

Convenience types that wrap Array around Device and Group respectively. Used when a configuration needs a list of devices or groups.

from pydoover.config import DevicesConfig, GroupsConfig

devices = DevicesConfig(
    "Managed Devices",
    description="Devices this processor has access to",
)
groups = GroupsConfig(
    "Device Groups",
    description="Groups to include in reports",
)

TagRef

A reference to a tag in another application. Contains four sub-fields: reference_name, agent_id, app_name, and tag_name. Used to wire up cross-application tag dependencies.

from pydoover.config import TagRef

external_temp = TagRef(
    "Temperature Source",
    description="Tag reference for the upstream temperature reading",
)

ApplicationPosition

A hidden integer element that controls the application's position in the UI layout. Smaller numbers appear closer to the top. Defaults to 100.

from pydoover.config import ApplicationPosition

position = ApplicationPosition(default=50)

ApplicationDefaultOpen

A hidden boolean element that controls whether the application section is expanded by default in the UI. When not set, the behaviour is dynamic based on the number of installed applications.

from pydoover.config import ApplicationDefaultOpen

default_open = ApplicationDefaultOpen(default=True)

LLMAPIKey

A hidden string element for storing LLM service API keys. Hidden from the UI by default.

from pydoover.config import LLMAPIKey

llm_key = LLMAPIKey(description="OpenAI API key for report generation")

JSON Schema Output

Each element's to_dict() method produces a JSON Schema fragment with standard and Doover-specific extensions.

A typical output for an Integer element looks like:

{
    "title": "GPIO Pin",
    "type": "integer",
    "x-name": "gpio_pin",
    "x-hidden": False,
    "x-required": True,
    "x-position": 0,
    "description": "BCM pin number",
    "minimum": 0,
    "maximum": 27,
}

The x- prefixed keys are custom extensions consumed by the Doover UI for rendering and validation beyond what standard JSON Schema provides.

Related Pages