Skip to main content
Skip table of contents

UI programming - Settings Definition File (SDF) Overview

1. Introduction

Flexa applications allow end-users to configure device settings through a web-based UI. Rather than building this UI by hand for every application, Barix provides a declarative approach: you describe your settings in a JSON file β€” the Settings Definition File (SDF) β€” and the Flexa platform renders the corresponding form automatically.

This guide explains the SDF format in detail, shows practical examples for each building block, and points you to the SDF Playground where you can author and test your files interactively.

1.1 What is an SDF?

SDF stands for Settings Definition File. It is a JSON file that acts as a form blueprint: it declares what settings exist, how they should be displayed, what values they accept, and how they relate to each other. The Flexa UI reads this file and generates a fully functional configuration form β€” no front-end coding required.

1.2 The SDF Playground

Barix provides an online playground where you can paste or write an SDF and instantly see the rendered form:

πŸ‘‰ http://sdf-playground.s3-website.eu-north-1.amazonaws.com/

Use it to prototype and iterate quickly before shipping your SDF.

πŸ’‘ Tip: The Playground gives you live feedback. If the form doesn't look right, check the browser console for JSON parsing errors


2. Package Structure & File Location

The sdf.json file must live inside a directory named config at the root of your Flexa Package. The filename must be exactly sdf.json β€” no other name is accepted.

CODE
myapp/
β”œβ”€β”€ config/
β”‚   └── sdf.json        ← your Settings Definition File
β”œβ”€β”€ manifest.json
β”œβ”€β”€ install.json
└── main.py

⚠️ Important: If sdf.json is missing, incorrectly named, or placed outside the config/ directory, the Flexa platform will not render a configuration form for your app.

To make the Flexa app β€œsdf aware,” the install.json must include the sdf object pointing to the directory containing the sdf.json file within the flexa package.

CODE
{
    "scripPath": "main.py",
    "run": "python3 main.py",
    "minFwVersion": "2.3.0",
    "name": "TCP_COMMAND_API",
    "files": [
    ],
    "sdf": {
        "file": "config/sdf.json",    <- Path of sdf.json within the package
        "tabName": "TCP API"
    }
} 

The β€œtabName” parameter will be the name of the TAB randered in the web UI. For the above example:

image-20260324-010052.png

3. Form Submission Behaviour

When a user fills in the form and clicks Submit, two things happen automatically:

  1. config.json is generated or updated β€” Flexa writes the resulting configuration values to a file named config.json at the root of your package directory. This is the file your application reads at runtime to obtain its configuration.

  2. The Flexa App restarts β€” the app is restarted automatically so it picks up the new config.json immediately. No manual restart is needed.

CODE
myapp/
β”œβ”€β”€ config/
β”‚   └── sdf.json        ← defines the form
β”œβ”€β”€ config.json         ← generated/updated on each form submission
β”œβ”€β”€ manifest.json
β”œβ”€β”€ install.json
└── main.py

Your application code should therefore read its configuration from config.json at the package root on startup. All values are nested inside a top-level "AppParam" object, regardless of how your SDF is structured. The keys inside it mirror the key fields you defined in your SDF params.

Example config.json created by a form generated using sdf (from the TCP Legacy API Flexa App):

CODE
{
    "AppParam": {
        "TCP_api_enable": true,
        "TCP_port": 12302,
        "TCP_timeout": 0,
        "allowed_ips": "",
        "password": "",
        "TCP_initial_state_subscriptions": "LocalIO",
        "TCP_add_IO_state_subscriptions": "None",
        "enable_syslog": true,
        "syslog_address": "192.168.2.6:514"
    }
}

⚠️ Important: Always read your configuration from config["AppParam"] in your application code β€” accessing the root object directly will not give you the parameter values.

πŸ’‘ Tip: Because the app restarts on every submission, make sure your app handles a clean startup gracefully β€” avoid assuming any in-memory state persisted from a previous run.


4. Top-Level Structure

Every SDF file is a JSON object with two top-level keys:

CODE
{
  "config": [ /* array of Content objects */ ],
  "scopes": [ /* array of Scope objects β€” reserved for future use */ ]
}

Key

Type

Required

Description

config

Content[]

Yes

The ordered list of items that make up the form. Each item is a Content object (Section, Param, or Division).

scopes

Scope[]

No

Reserved for future use. Define named scopes here for advanced cross-parameter validation. Pass an empty array for now.


5. Content Objects

Every element that appears in the form is a Content object. A Content object always has exactly two keys:

CODE
{
  "c_type": "section" | "param" | "division",
  "c_data":  <Section object> | <Param object> | <Division object>
}

c_type value

What it renders

c_data type

section

An expandable panel grouping related fields

Section object

param

A single configurable field (text box, toggle, dropdown…)

Param object

division

A visual horizontal divider with an optional label

Division object


6. Sections

A Section groups related parameters under an expandable panel. Sections can be nested β€” a section's content can itself contain other sections.

6.1 Section Fields

Field

Type

Required

Description

label

string

Yes

The text displayed in the section header.

key

string

Yes

A unique identifier for this section. Used as a namespace key in the resulting JSON output. Params inside a section are written to config.json nested under this key.

content

Content[]

Yes

The items inside the section: a mix of Params, sub-Sections, or Divisions.

icon

string

No

A Material Icon code to display next to the label (e.g. "settings", "wifi", "volume_up").

panelOpenState

boolean

No

Whether the section starts expanded. Defaults to true.

hideHeader

boolean

No

When true, the section header is hidden β€” useful for top-level grouping.

6.2 Example

A collapsible "Network" section containing two parameters:

CODE
{
  "config": [
    {
      "c_type": "section",
      "c_data": {
        "label": "Network",
        "key": "network",
        "icon": "wifi",
        "panelOpenState": true,
        "content": [
          {
            "c_type": "param",
            "c_data": {
              "label": "IP Address",
              "key": "ip_address",
              "var_type": "string",
              "type": "text",
              "type_properties": {
                "widget": "input",
                "regexp": "^\\d{1,3}(\\.\\d{1,3}){3}$"
              },
              "default_value": ""
            }
          },
          {
            "c_type": "param",
            "c_data": {
              "label": "Port",
              "key": "port",
              "var_type": "number",
              "type": "number",
              "type_properties": {
                "widget": "input",
                "min": 1,
                "max": 65535
              },
              "default_value": 12345
            }
          }
        ]
      }
    }
  ],
  "scopes": []
}

7. Parameters (Params)

A Param is the core building block of any SDF. It maps a single user-facing form field to a key in the resulting configuration JSON.

7.1 Common Param Fields

Field

Type

Required

Description

label

string

Yes

The field label shown to the user.

key

string

Yes

The output key written to the resulting JSON configuration.

var_type

string

Yes

The data type of the stored value. See Section 7.2.

type

string

Yes

The UI widget category. See Section 7.3.

type_properties

object

Yes

Widget-specific configuration. See Section 7.3.

tooltip

string

No

A short hint shown on hover.

help

string

No

Markdown-formatted help text shown in a help panel. Falls back to tooltip if omitted.

placeholder

string

No

Placeholder text shown inside an empty input field.

required

boolean

No

When true, the form cannot be submitted without a value. For boolean params, required: true means the checkbox must be checked.

default_value

any

No

The value pre-filled in the field. Must match the var_type.

conditional

array

No

Conditionally reveal other content based on this param's value. See Section 8.

preserveUnselected

boolean

No

If true, values hidden by a condition are preserved so they can be restored when the condition becomes active again.

scope

string

No

Associates this param with a named scope (see scopes in Section 2).

7.2 Variable Types (var_type)

var_type declares the data type of the value stored in the output JSON.

var_type

Description

Typical UI type

string

Plain text: names, URLs, email addresses, etc.

text or selection

number

Integer or decimal number.

number

boolean

True / false value.

boolean

string[]

An ordered list of strings (multi-select).

selection

number[]

An ordered list of numbers (multi-select).

selection

any[]

An ordered list of heterogeneous objects.

list

7.3 UI Types and their type_properties

The type field chooses the widget category. Each type expects specific keys inside type_properties.

7.3.1 text

Renders a single-line text input. Use this for strings such as hostnames, email addresses, or any value that needs format validation via a regular expression.

Property

Required

Description

widget

Yes

Must be "input".

regexp

Yes

A regular expression string used to validate the value. The form blocks submission when validation fails.

Example β€” hostname field:

CODE
{
  "label":       "Hostname",
  "key":         "hostname",
  "var_type":    "string",
  "type":        "text",
  "tooltip":     "The device hostname (letters, digits, hyphens only)",
  "placeholder": "my-device",
  "required":    true,
  "type_properties": {
    "widget": "input",
    "regexp": "^[a-zA-Z0-9-]+$"
  }
}

7.3.2 password

Identical to text but the entered value is masked. Use for secrets, API keys, and passwords.

CODE
{
  "label":    "API Secret",
  "key":      "api_secret",
  "var_type": "string",
  "type":     "password",
  "required": true,
  "type_properties": {
    "widget": "input",
    "regexp": ".+"
  }
}

7.3.3 number

Renders either a numeric text input or a slider. Use this for all numeric settings such as volumes, timeouts, and port numbers.

Property

Required

Description

widget

Yes

"input" for a text box, "slider" for a range slider.

min

No

Minimum accepted value.

max

No

Maximum accepted value.

step

No

Stepping interval (e.g. 1 for integers, 0.1 for one decimal place).

units

No

Unit label displayed next to the field (e.g. "ms", "dB").

discrete

No

Slider only. Shows the numeric value label on the thumb. Default: false.

showTickMarks

No

Slider only. Shows tick marks along the track. Default: false.

Example β€” volume slider (0–100 dB):

CODE
{
  "label":         "Output Volume",
  "key":           "volume",
  "var_type":      "number",
  "type":          "number",
  "default_value": 80,
  "type_properties": {
    "widget":        "slider",
    "min":           0,
    "max":           100,
    "step":          1,
    "units":         "dB",
    "discrete":      true,
    "showTickMarks": false
  }
}

7.3.4 boolean

Renders a checkbox. The stored value is true or false.

⚠️ Note: When required is set to true for a boolean param, the checkbox must be checked for the form to be valid. This is useful for consent or acknowledgement fields.

CODE
{
  "label":         "Enable Streaming",
  "key":           "streaming_enabled",
  "var_type":      "boolean",
  "type":          "boolean",
  "default_value": false,
  "type_properties": {
    "widget": "checkbox"
  }
}

7.3.5 selection

Renders a dropdown (or multi-select dropdown when var_type is string[] or number[]). You define the list of options explicitly.

Option field

Required

Description

value

Yes

The raw value stored in the configuration.

label

Yes

The human-readable label shown in the dropdown.

tooltip

No

A tooltip shown when hovering this option.

disabled

No

Always disables this option.

disabledIf

No

Conditionally disables the option. Requires paramReference, operator, and value sub-fields.

Example β€” audio codec selection:

CODE
{
  "label":    "Audio Codec",
  "key":      "codec",
  "var_type": "string",
  "type":     "selection",
  "required": true,
  "type_properties": {
    "widget": "select",
    "options": [
      { "value": "mp3",  "label": "MP3" },
      { "value": "aac",  "label": "AAC" },
      { "value": "opus", "label": "Opus", "tooltip": "Best for low-latency" },
      { "value": "pcm",  "label": "PCM (uncompressed)", "disabled": true }
    ]
  }
}

7.3.6 list

Renders a dynamic list of identical sub-forms. Each item in the list is itself a set of Content objects, allowing the user to add, remove, and reorder structured entries.

Property

Required

Description

config

Yes

Array of Content objects that define the fields for each list item.

max_items

No

Maximum number of items the user may add.

min_items

No

Minimum number of items required. (Not yet implemented.)

initial_items

No

Number of empty items shown on load. (Not yet implemented.)

Example β€” list of audio stream destinations:

CODE
{
  "label":    "Stream Destinations",
  "key":      "destinations",
  "var_type": "any[]",
  "type":     "list",
  "type_properties": {
    "max_items": 4,
    "config": [
      {
        "c_type": "param",
        "c_data": {
          "label": "URL", "key": "url", "var_type": "string",
          "type": "text",
          "type_properties": { "widget": "input", "regexp": "^https?://.+" }
        }
      },
      {
        "c_type": "param",
        "c_data": {
          "label": "Port", "key": "port", "var_type": "number",
          "type": "number",
          "type_properties": { "widget": "input", "min": 1, "max": 65535 }
        }
      }
    ]
  }
}

8. Conditional Content

The conditional field lets you show or hide additional Content objects depending on the current value of a Param. This keeps the form clean β€” only relevant settings are visible at any given time.

8.1 How it works

Add a conditional array to any Param. Each entry specifies one or more match conditions and a config list of Content objects to reveal when those conditions are met.

CODE
"conditional": [
  {
    "when_value_is": ["udp", "tcp"],
    "config": [
      {
        "c_type": "param",
        "c_data": { "label": "Remote IP", "key": "remote_ip", ... }
      }
    ]
  }
]

⚠️ Important: Conditional content is inserted into the same parent as the param that owns the conditional. If two conditions can be active simultaneously, make sure all their child param keys are unique to avoid collisions.

8.2 Condition Keys

Condition key

Applicable var_type

Description

when_value_is

any

Show when the value equals one of the listed values.

when_value_is_not

any

Show when the value does not equal any of the listed values.

when_value_is_gt

number

Show when the value is greater than the given number.

when_value_is_lt

number

Show when the value is less than the given number.

when_value_is_ge

number

Show when the value is greater than or equal to the given number.

when_value_is_le

number

Show when the value is less than or equal to the given number.

when_value_starts_with

string, number

Show when the value starts with one of the listed prefixes.

when_value_includes

string, string[], number

Show when the value contains one of the listed substrings or items.

when_value_matches_regexp

string

Show when the value matches one of the listed regular expressions.

8.3 Example β€” Protocol-dependent fields

A transport protocol selector reveals different sub-settings depending on the chosen protocol:

CODE
{
  "c_type": "param",
  "c_data": {
    "label":    "Transport Protocol",
    "key":      "protocol",
    "var_type": "string",
    "type":     "selection",
    "required": true,
    "type_properties": {
      "widget":  "select",
      "options": [
        { "value": "udp",  "label": "UDP"  },
        { "value": "tcp",  "label": "TCP"  },
        { "value": "http", "label": "HTTP" }
      ]
    },
    "conditional": [
      {
        "when_value_is": ["udp", "tcp"],
        "config": [
          { "c_type": "param", "c_data": {
              "label": "Remote Host", "key": "remote_host",
              "var_type": "string", "type": "text",
              "type_properties": { "widget": "input", "regexp": ".+" }
          }}
        ]
      },
      {
        "when_value_is": ["http"],
        "config": [
          { "c_type": "param", "c_data": {
              "label": "Endpoint URL", "key": "http_url",
              "var_type": "string", "type": "text",
              "type_properties": { "widget": "input", "regexp": "^https?://.+" }
          }}
        ]
      }
    ]
  }
}

9. Divisions

A Division is a visual horizontal rule that separates groups of parameters within a section. It helps the user navigate long forms without introducing a new expandable panel.

CODE
{
  "c_type": "division",
  "c_data": {
    "label": "Advanced Settings",
    "icon":  "tune"
  }
}

Field

Required

Description

label

Yes

Text displayed alongside the divider.

icon

No

A Material Icon code to display next to the label.


10. Complete Example

The following SDF defines a realistic audio streaming configuration form with two sections, several param types, and a conditional:

CODE
{
  "config": [
    {
      "c_type": "section",
      "c_data": {
        "label": "General",
        "key":   "general",
        "icon":  "settings",
        "content": [
          {
            "c_type": "param",
            "c_data": {
              "label":       "Device Name",
              "key":         "device_name",
              "var_type":    "string",
              "type":        "text",
              "required":    true,
              "placeholder": "My Barix Device",
              "type_properties": { "widget": "input", "regexp": ".+" },
              "default_value": "Barix"
            }
          },
          {
            "c_type": "param",
            "c_data": {
              "label":         "Enable Streaming",
              "key":           "streaming_enabled",
              "var_type":      "boolean",
              "type":          "boolean",
              "default_value": true,
              "type_properties": { "widget": "checkbox" }
            }
          }
        ]
      }
    },
    {
      "c_type": "section",
      "c_data": {
        "label": "Audio",
        "key":   "audio",
        "icon":  "volume_up",
        "content": [
          {
            "c_type": "param",
            "c_data": {
              "label":         "Output Volume",
              "key":           "volume",
              "var_type":      "number",
              "type":          "number",
              "default_value": 80,
              "type_properties": {
                "widget": "slider", "min": 0, "max": 100,
                "step": 1, "units": "dB", "discrete": true
              }
            }
          },
          {
            "c_type": "param",
            "c_data": {
              "label":    "Codec",
              "key":      "codec",
              "var_type": "string",
              "type":     "selection",
              "required": true,
              "type_properties": {
                "widget": "select",
                "options": [
                  { "value": "mp3",  "label": "MP3"  },
                  { "value": "aac",  "label": "AAC"  },
                  { "value": "opus", "label": "Opus" }
                ]
              },
              "conditional": [
                {
                  "when_value_is": ["mp3"],
                  "config": [
                    { "c_type": "param", "c_data": {
                        "label": "Bitrate (kbps)", "key": "mp3_bitrate",
                        "var_type": "number", "type": "selection",
                        "type_properties": {
                          "widget": "select",
                          "options": [
                            { "value": 128, "label": "128 kbps" },
                            { "value": 192, "label": "192 kbps" },
                            { "value": 320, "label": "320 kbps" }
                          ]
                        }
                    }}
                  ]
                }
              ]
            }
          }
        ]
      }
    }
  ],
  "scopes": []
}

This sdf configuration, after submitting from the UI tab, produces the following config.json :

CODE
{
    "AppParam": {
      "general": {
        "device_name": "Barix",
        "streaming_enabled": true
      },
      "audio": {
        "volume": 80,
        "mp3_bitrate": 128,
        "codec": "mp3"
      }
    }
}

11. Quick Reference

11.1 Choosing the right type

Use case

var_type

type

widget

Free-form text, URL, hostname

string

text

input

Secret / password

string

password

input

Integer or decimal

number

number

input

Range with a visual slider

number

number

slider

Yes / no toggle

boolean

boolean

checkbox

Single choice from a list

string

selection

select

Multiple choices from a list

string[]

selection

select

Repeating structured items

any[]

list

(n/a)

11.2 Mandatory fields at a glance

Object

Mandatory fields

Root

config

Content

c_type, c_data

Section

label, key, content

Param

label, key, var_type, type, type_properties

Division

label

Scope

name


12. Tips & Best Practices

βœ… Use the Playground first. Paste your SDF into the Playground before integrating it into your app. It catches structural errors instantly.

βœ… Keep keys unique across siblings. Param keys must be unique within the same parent. When conditionals bring in extra params, verify their keys don't clash with sibling params β€” especially when multiple conditions can be active at the same time.

βœ… Always provide a default_value for optional fields. Users shouldn't have to fill in every field from scratch. Supply sensible defaults to reduce friction and mistakes.

βœ… Use tooltip and help together. tooltip gives a one-line hint on hover. help allows full Markdown β€” use it for longer explanations, links to documentation, or example values.

βœ… Prefer selection over free text where possible. Offering a fixed set of valid values reduces misconfiguration errors compared to an open text field with a complex regex.

⚠️ scopes are not yet implemented. Include it as an empty array ("scopes": []) to keep your SDF forward-compatible.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.