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 theconfigurationdict.type— one of the field types below.label— text shown in the form.default— required even when there is no useful value; usenullin 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 forselect,radio, andmultiselect; 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)
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
- Place the device on the system block diagram.
- Select it. If
fileio.path("configuration setup")defines fields, a Configuration section appears in the properties panel. - Set the values for that instance. They are stored as JSON in a
<configuration>child of the device group. - On system build, those answers are written to the BOM
configurationcolumn 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.
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.