Skip to content

Device Configurations

Harnice treats a part number as the unique identity of a component's design. If a released or otherwise established part changes in form, fit, or function, that change needs a new part number. Drawing revisions and documentation updates do not.

A configuration records how to set up a piece of hardware. It describes how an item is used, not what the item is. Record as much of that setup as you can. A field is worth keeping when the build never reads it. The instance still stores the answer, and another program can read it from the BOM.

Some answers change what Harnice builds. A script may use a field for any reason. Two examples are a device signals list that follows the answers, and a system channel map that follows the answers. Other answers are setup notes: a gain knob position, a dip-switch pattern, a menu preset, a firmware option.

Examples:

  • Power supply with multiple output voltage settings
  • Audio preamplifier that works for both balanced or unbalanced inputs
  • Digitally configurable analog-to-digital converter input
  • Antenna used as a transmitter or a receiver
  • A gain setting, a dip switch, or a menu preset that the build leaves unread

In those cases the same part is used differently without rebuilding or rewiring it, so Harnice keeps one device part number and records the setup as a configuration.

A configuration does not filter alternate rows out of a signals list. Build instructions write one signals list for the answers currently in configuration (a dict). Validation only sees that list.

Do not use configuration to change the physical part. If the connector or the hardware build changes, that is a new part number. A pin is on the signals list only when a channel in this configuration uses it. Omitting a pin this configuration does not use is correct. A channel that is present still includes every signal its channel type defines.

Where configuration lives

A new device gets a blank fileio.path("configuration setup"). Add a field for each setup choice you want the instance to remember.

Place File Role
Device revision fileio.path("configuration setup") Declares the form: field ids, types, defaults, validation, and show_if rules
Device build fileio.path("build instructions") Reads configuration["field_id"] for any reason. One example is writing the signals list for those answers. A field the script never reads is still stored
Device symbol fileio.path("block diagram symbol") Text is a Python f-string. {configuration.get("field_id")} reads a field
System block diagram <configuration> under the placed device <g> Instance answers as JSON
System BOM configuration column Compact JSON object for that device. Parse the cell, then key by field id
System instances list device_configuration The same answers after the BOM is imported
Placed instance copy that instance's fileio.path("configuration setup") The device's setup form, used to render the instance editor

When you build a device on its own, Harnice fills configuration from each field's default in fileio.path("configuration setup"). When a system rebuilds a placed device, instance answers from the diagram / BOM override those defaults. Missing keys are still filled from the setup defaults so fileio.path("build instructions") always sees a complete dict.

show_if only controls whether a field is visible in the form. Hidden fields still contribute their defaults.

Editing the form

Open the device in the Harnice console and select configuration setup. The visual editor lets you drop field types, set id / label / default / required, add options, and write show_if rules. Saving writes fileio.path("configuration setup").

You can also edit fileio.path("configuration setup") directly. A comment-only or empty file is valid and means the device has no configuration fields.

YAML shape

Top level must be a mapping with a fields list. Each field is a mapping.

Required on every field:

  • id — unique string. This is the key in the configuration dict.
  • type — one of the field types below.
  • label — text shown in the form.
  • default — required even when there is no useful value; use null in that case.

Optional on every field:

  • required — boolean.
  • validation — type-specific constraints (see below).
  • show_if — show this field only when another field matches a condition.
  • options — required for select, radio, and multiselect; not allowed on other types.

select / radio defaults must be one of the declared option values (or null). A multiselect default must be a list of option values (often []). A list default must be a list of strings (often []).

json, yaml, and toml defaults are either the value itself or text in that format. A string default is the document text and must parse. Any other YAML value is used as the data. Empty text is None when the device builds. toml data must be a mapping, because a TOML document is a table.

Field types

type Typical default Notes
text "" Short string
textarea "" Long string
json "{}" or a mapping Parsed with json.loads before build instructions run
yaml "{}" or a mapping Parsed with YAML safe_load when the answer is text
toml "" or a mapping Parsed with tomllib.loads when the answer is text. Needs Python 3.11 or newer
list [] List of strings
url "" Use validation.format: uri
number a number
select one option value Dropdown; requires options
radio one option value Requires options
multiselect [] Checkboxes; requires options
boolean false
slider a number in range Also accept scale and rating
scale a number in range
rating a number in range
ip_addr "" Use format: ipv4 or ipv6
mac_addr "" Use format: mac
channel_type a channel-type tuple string Use format: channel_type
matrix No extra validation keys

A channel_type default is the same (id, repo) tuple string used elsewhere, for example "(1, 'https://github.com/harnice/harnice-general-library')".

Validation

Only these keys are allowed under validation, and only on the types listed. errorMessages and custom may sit alongside them.

Field type Allowed validation keys
text, textarea minLength, maxLength, pattern, format
url format
number minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
multiselect, list minItems, maxItems, uniqueItems
slider, scale, rating minimum, maximum; slider also allows multipleOf
ip_addr pattern, format
mac_addr, channel_type format
select, radio, boolean, matrix, json, yaml, toml none

Valid format values: email, uri, date, date-time, time, uuid, ipv4, ipv6, hostname, mac, channel_type.

pattern must be a valid regular expression. minimum may not exceed maximum (same for minLength / maxLength and minItems / maxItems).

Conditional fields (show_if)

show_if:
  field: "other_field_id"
  operator: equals
  value: "some_answer"

field must be another field id. value is required unless the operator is is_empty or is_not_empty.

Operators: equals, not_equals, contains, not_contains, greater_than, less_than, greater_than_or_equal, less_than_or_equal, is_empty, is_not_empty, starts_with, ends_with.

Field type reference

Copy a block into fields: and change id, label, and default.

# --- short text ---
- id: "device_name"
  type: text
  label: "Device name"
  required: true
  default: ""
  validation:
    minLength: 2
    maxLength: 60
    pattern: "^[A-Za-z0-9\\- ]+$"

# --- long text ---
- id: "install_notes"
  type: textarea
  label: "Installation notes"
  default: ""
  validation:
    maxLength: 500

# --- url ---
- id: "firmware_url"
  type: url
  label: "Firmware update URL"
  default: ""
  validation:
    format: uri

# --- number ---
- id: "port"
  type: number
  label: "Communication port"
  default: 502
  validation:
    minimum: 1
    maximum: 65535
    multipleOf: 1

# --- ip address ---
- id: "device_ip"
  type: ip_addr
  label: "Device IP address"
  default: ""
  validation:
    format: ipv4          # or: ipv6

# --- mac address ---
- id: "device_mac"
  type: mac_addr
  label: "Device MAC address"
  default: ""
  validation:
    format: mac

# --- channel type ---
- id: "channel_type"
  type: channel_type
  label: "Channel type"
  required: true
  default: "(1, 'https://github.com/harnice/harnice-general-library')"
  validation:
    format: channel_type

# --- single choice, dropdown ---
- id: "input_type"
  type: select
  label: "Input type"
  required: true
  default: "xlr"          # must be one of the option values below
  options:
    - value: "xlr"
      label: "XLR (Balanced)"
    - value: "trs"
      label: "TRS (Balanced)"
    - value: "rca"
      label: "RCA (Unbalanced)"

# --- single choice, radio buttons ---
- id: "state"
  type: radio
  label: "Device state"
  default: "standby"
  options:
    - value: "active"
      label: "Active"
    - value: "standby"
      label: "Standby"
    - value: "disabled"
      label: "Disabled"

# --- multiple choice, checkboxes ---
- id: "enabled_outputs"
  type: multiselect
  label: "Enabled outputs"
  default: []
  options:
    - value: "main"
      label: "Main"
    - value: "monitor"
      label: "Monitor"
    - value: "aux"
      label: "Aux"
  validation:
    minItems: 1
    maxItems: 3

# --- yes/no toggle ---
- id: "phantom_power"
  type: boolean
  label: "48V phantom power"
  default: false

# --- linear scale / slider ---
- id: "gain"
  type: slider              # or: scale / rating
  label: "Input gain (dB)"
  default: 0
  validation:
    minimum: -12
    maximum: 12
    multipleOf: 0.5

# --- json document ---
- id: "presets"
  type: json
  label: "Presets"
  default: |
    {
      "gain": 0
    }

# --- yaml document ---
# a mapping written directly is the value; a string is parsed as YAML text
- id: "routing"
  type: yaml
  label: "Routing"
  default:
    mode: "auto"
    gain: 0

# --- toml document ---
- id: "service"
  type: toml
  label: "Service"
  default: |
    port = 502
    host = "0.0.0.0"

# --- list of strings ---
- id: "labels"
  type: list
  label: "Labels"
  default:
    - "main"
    - "monitor"
  validation:
    minItems: 1

# --- only show based on a previous answer ---
# add this block under any field:
#   show_if:
#     field: "other_field_id"
#     operator: equals
#     value: "some_answer"

Worked example

A full electrical-device form. This is reference only; a new device starts blank.

fields:

  - id: "device_name"
    type: text
    label: "Device name"
    required: true
    default: ""
    validation:
      minLength: 2
      maxLength: 60

  - id: "device_ip"
    type: ip_addr
    label: "Device IP address"
    required: true
    default: ""
    validation:
      format: ipv4

  - id: "device_subnet"
    type: ip_addr
    label: "Subnet mask"
    required: true
    default: "255.255.255.0"
    validation:
      format: ipv4

  - id: "device_mac"
    type: mac_addr
    label: "Device MAC address"
    required: false
    default: ""
    validation:
      format: mac

  - id: "port"
    type: number
    label: "Communication port"
    required: true
    default: 502
    validation:
      minimum: 1
      maximum: 65535
      multipleOf: 1

  - id: "input_type"
    type: select
    label: "Input type"
    required: true
    default: "xlr"
    options:
      - value: "xlr"
        label: "XLR (Balanced)"
      - value: "trs"
        label: "TRS (Balanced)"
      - value: "rca"
        label: "RCA (Unbalanced)"
      - value: "optical"
        label: "Optical (S/PDIF)"

  - id: "gain"
    type: slider
    label: "Input gain (dB)"
    required: false
    default: 0
    validation:
      minimum: -12
      maximum: 12
      multipleOf: 0.5

  - id: "phantom_power"
    type: boolean
    label: "48V phantom power"
    required: false
    default: false
    # only relevant for balanced mic-level inputs
    show_if:
      field: "input_type"
      operator: equals
      value: "xlr"

  - id: "state"
    type: select
    label: "Device state"
    required: true
    default: "standby"
    options:
      - value: "active"
        label: "Active"
      - value: "standby"
        label: "Standby"
      - value: "disabled"
        label: "Disabled"

Using answers in build instructions

Inside the device's fileio.path("build instructions"), read a field with configuration["field_id"] for any reason. A system script that needs the same answers reads the BOM row. Choosing a channel_map.map is one example. Both are on Reading Device Configurations.

Harnice injects configuration into fileio.path("build instructions"). On a standalone device build it is the setup defaults. On a system rebuild of a placed device it is that instance's answers, with setup defaults filling any missing keys.

Harnice parses json, yaml, and toml text before the build script runs. configuration["field_id"] is the loaded data (dict, list, string, number, boolean, or None). A list field is a list of strings. Empty text is None.

# configuration is a dict keyed by field id
input_type = configuration["input_type"]
phantom = configuration["phantom_power"]
presets = configuration["presets"]   # dict or list, from a json, yaml, or toml field
labels = configuration["labels"]     # list of strings

if input_type == "xlr":
    channel_type = ch_type_ids["balanced_in"]
else:
    channel_type = ch_type_ids["unbalanced_in"]

The branch above is one example: the answers change the signals list. One field is enough for a device that is either balanced or unbalanced. A mixing console might have a field per input. Add further fields for setup the build never uses, such as a gain knob or a menu preset.

Filling answers in a system

  1. Place the device on the system block diagram.
  2. Select it. If fileio.path("configuration setup") defines fields, a Configuration section appears in the properties panel.
  3. Set the values for that instance. They are stored as JSON in a <configuration> child of the device group.
  4. On system build, those answers are written to the BOM configuration column and passed into that instance's device rebuild.

If the placed device has no <configuration> child yet, Harnice seeds it from the setup defaults so the BOM and the properties panel stay in sync.

Reading one device from the BOM

Another program reads a placed device's answers from the system BOM. The steps, the columns, and what each field type looks like after parsing are on Reading Device Configurations.

Showing values on the symbol

Text in the device block diagram symbol is a Python f-string. The symbol keeps the expression. When a system builds that device, Harnice evaluates the f-string in the device build namespace and writes the resulting string into the system diagram.

configuration is the same dict the build instructions see. refdes is the instance name on the diagram. Other names already available in build instructions (fileio, state, and the list and utility modules) are available too, including names assigned while build.py runs.

{refdes}
{configuration.get("input_type")}
{configuration.get("phantom_power")}

A missing key is None, so configuration.get("input_type") or "" leaves that blank. Lists and booleans format the way Python formats them (True, False, ['a', 'b']). \n starts a new line. \\n stays as the two characters \n. Text with no {...} and no \n is left as written.