Skip to main content

BB-Eco template JSON format

This page documents the JSON file format used by bb-eco template export, bb-eco template apply, bb-eco template diff, and the desktop app's Template tab.

For the concepts behind config as code see Configuration as code with BB-Eco. For the CLI commands that read and write this format, see the bb-eco template reference.

Top-level shape

{
"identity": { /* DeviceIdentitySection — read-only, optional */ },
"network": { /* NetworkTemplateSection — DHCP, gateway, etc */ },
"device": { /* DeviceTemplateSection — name, location */ },
"security": { /* SecurityTemplateSection — credentials, MAC filter */ },
"io": { /* IoTemplateSection — ED only */ },
"serialPorts": [ /* SerialPortTemplateSection[] — ES only */ ],
"version": 1,
"family": "ED",
"compatibleModels": ["ED-588", "ED-549"],
"description": "Production ED I/O baseline — Modbus TCP, DHCP",
"createdAt": "2026-05-05T15:42:00Z"
}

Every section and every field is optional. A null field or a missing section means don't touch — BB-Eco leaves the corresponding setting on the target device unchanged.

Envelope fields

Five top-level fields describe the template itself, not the device configuration:

FieldTypeDefaultDescription
versioninteger1Template schema version. Bumped only when the format changes incompatibly.
familystring | nullfrom device on exportTarget device family. "ED" or "ES". Apply will refuse to push an ED template to an ES device, and vice versa.
compatibleModelsstring[] | nullnullOptional model allowlist. If set, apply will refuse to push to a device whose model isn't in the list. Useful for templates that include settings only valid on a specific hardware variant.
descriptionstring | nullnullHuman-readable description. Shown in the desktop app's template list and in bb-eco template validate output.
createdAtRFC 3339 timestamp | nullexport timeWhen the template was generated. Informational only.

These envelope fields are suppressed from bb-eco config <device> output but retained in bb-eco template export output.

identity — read-only device identity

Populated only when --include-identity is passed to export. Never applied by template apply. Never compared by template diff. Useful as a forensic record of which device a template came from.

"identity": {
"mac": "00:0A:4F:12:34:AB",
"ip": "192.168.1.50",
"model": "ED-588",
"modelVariant": "T",
"firmwareVersion": "8.33",
"firmwareType": "0001",
"firmwareDate": "2026-01-15"
}
FieldTypeDescription
macstringHardware MAC, colon-separated uppercase
ipstringAddress the device was reachable at when exported
modelstringModel number (e.g. ED-588)
modelVariantstringVariant suffix (T, T4, R2, R3)
firmwareVersionstringRunning firmware version
firmwareTypestringNumeric type code from the .efw SINF header
firmwareDatestringFirmware build date

network — network configuration

"network": {
"dhcp": true,
"staticIp": null,
"subnet": "255.255.255.0",
"gateway": "192.168.1.1",
"dns": "192.168.1.1",
"webPort": 80,
"upnp": true
}
FieldTypeNotes
dhcpboolean | nullIf true, ignores static settings
staticIpstring | nullTypically null in templates — IPs are device-specific
subnetstring | nullDotted-quad subnet mask
gatewaystring | nullDefault gateway IP
dnsstring | nullPrimary DNS server
webPortinteger | nullWeb server port (default 80)
upnpboolean | nullED only — UPnP enabled

network is applied last in the apply order so that an IP change can't strand the rest of the apply.

device — device-level identity settings

"device": {
"name": "Pumphouse-Sensors-01",
"location": "Pumphouse",
"checkConnectivity": true
}
FieldTypeNotes
namestring | nullUser-facing device name
locationstring | nullFree-form location label. ED firmware caps this at 10 characters
checkConnectivityboolean | nullConnectivity-check feature enabled

security — credentials and access control

Excluded from export by default. Pass --include-security to opt in for usernames, MAC filters, and auth method. Passwords stay excluded unless you also pass --include-passwords (rarely a good idea — see the warning below).

"security": {
"adminUsername": "admin",
"adminPassword": null,
"userUsername": null,
"userPassword": null,
"adminPasswordSet": true,
"userPasswordSet": false,
"adminMacFilterEnabled": false,
"adminMacAddress": null,
"userMacFilterEnabled": true,
"userMacAddresses": ["00:0A:4F:11:22:33", "00:0A:4F:44:55:66"],
"authentication": "Digest",
"resetProtection": "AdminOnly"
}
FieldTypeNotes
adminUsername, userUsernamestring | nullAccount usernames
adminPassword, userPasswordstring | nullNever commit these to Git
adminPasswordSet, userPasswordSetboolean | nullRead-only signals — true if device has a password set. Ignored by apply
adminMacFilterEnabled, userMacFilterEnabledboolean | nullMAC-based access control toggles
adminMacAddressstring | nullAllowed admin MAC, hex (e.g. 00A04F010203)
userMacAddressesstring[] | nullWhitelist of up to 32 user MAC addresses
authenticationstring | null"Basic" or "Digest"
resetProtectionstring | null"Everybody" or "AdminOnly"
Don't commit passwords

The export tool can embed passwords in the JSON when you ask it to, but storing passwords in version control is rarely the right answer. Prefer leaving adminPassword and userPassword as null in committed templates and setting them through a separate secure-secrets workflow.

io — IO protocol configuration (ED only)

"io": {
"protocol": "Modbus",
"modbusTcpPort": 502,
"modbusSlaveId": 1,
"modbusIdleTimeout": 60,
"maxConnections": 16,
"checksum": 1,
"lines": [
{
"index": 0,
"name": "Pump 1 status",
"lineType": "digitalInput",
"enabled": true
},
{
"index": 1,
"name": "Tank temperature",
"lineType": "rtd",
"enabled": true,
"sensorType": "Pt100-385",
"wiring": "3-wire",
"unit": "°C"
}
]
}

Top-level fields:

FieldTypeNotes
protocolstring | null"ASCII" or "Modbus"
dconAddressint | nullDCON address (1-based)
dconTcpPortint | nullDCON TCP port (default 9500)
maxConnectionsint | nullMax simultaneous connections
maxConnectionsPerDeviceint | nullPer-client connection cap
idleTimeoutint | nullIdle timeout in seconds (0 = disabled)
counterUpdateint | nullCounter update direction code
checksumint | nullChecksum enabled (0/1)
modbusTcpPortint | nullModbus TCP port (default 502)
modbusSlaveIdint | nullModbus slave ID
modbusIdleTimeoutint | nullModbus idle timeout
gatewayBaudRateint | nullBaud rate code for serial gateway
commandTimeoutint | nullCommand timeout in ms
powerOnValuesstring | nullPower-on IO line values, hex string
safeValuesstring | nullSafe-disconnect IO values, hex string
eStopValuesstring | nullE-stop IO values, hex string
eStopLine, eStopDirectionstring | nullE-stop trigger configuration
linesIoLineTemplateConfig[] | nullPer-line config (see below)

Each entry in lines:

FieldTypeNotes
indexintLine index (0-based). Required when present
namestring | nullHuman-readable line name
lineTypestring | null"digitalInput", "digitalOutput", "analogInput", "analogOutput", "rtd", "thermocouple"
enabledboolean | nullLine enabled
unitstring | nullDisplay unit ("°C", "V", "mA")
sensorTypestring | nullRTD: "Pt100-385" etc. Analog: "+/-10V", "4-20mA". Thermocouple: "K", "J", "T", etc
wiringstring | nullRTD only: "2-wire", "3-wire", "4-wire"

serialPorts — serial port configuration (ES only)

Array of port configurations:

"serialPorts": [
{
"index": 0,
"portMode": "RS422/485",
"overrideAppSettings": true,
"baudRate": 115200,
"dataBits": 8,
"parity": "None",
"stopBits": "One",
"flowControl": "None",
"duplex": "HalfDuplexAutoGating",
"protocol": "RawTcp",
"tcpPort": 9001,
"idleTimeout": 60,
"fifoEnabled": true,
"tunneling": {
"enabled": true,
"isMaster": false,
"remoteIp": null,
"remotePort": null
}
}
]
FieldTypeNotes
indexint0-based port index. Required when present
portModestring | null"RS232" or "RS422/485"
overrideAppSettingsboolean | null"Always use these settings" toggle
baudRateint | nullStandard baud rates
dataBitsint | null5, 6, 7, or 8
paritystring | null"None", "Odd", "Even", "Mark", "Space"
stopBitsstring | null"One", "OnePointFive", "Two"
flowControlstring | null"None", "CtsRts", "XonXoff", "DtrDsr"
duplexstring | null"FullDuplex", "HalfDuplexAutoGating", etc
protocolstring | null"RawTcp", "TelnetServer", etc
tcpPortint | nullTCP port for the serial protocol
idleTimeoutint | nullIdle timeout in seconds
fifoEnabledboolean | nullUART FIFO buffer toggle
tunnelingTunnelTemplateConfig | nullSee below

tunneling shape:

FieldTypeNotes
enabledbooleanTunnel mode on/off
isMasterbooleanTrue = initiates connection, false = listens
remoteIpstring | nullRemote tunnel IP (for master)
remotePortint | nullRemote tunnel TCP port

Apply order

When bb-eco template apply runs, it pushes the sections to the device in this order to avoid losing connectivity mid-apply:

  1. device — name, location (no connection impact)
  2. security — credentials (no connection impact)
  3. io — ED line config (no connection impact)
  4. serialPorts — ES port config (no connection impact)
  5. network.webPort and network.upnpmay disconnect if the web port changes
  6. network.dhcp, network.staticIp, network.subnet, network.gateway, network.dnsmay disconnect if the IP changes

If the device becomes unreachable after step 5 or 6, apply treats this as a successful apply (the new settings took effect — you just need to find the device at its new address).

Versioning

version: 1 covers all current templates. The version field exists so a future BB-Eco can refuse to apply templates from a newer schema and so older BB-Eco versions can warn customers about format changes. Don't bump it manually — BB-Eco writes the right value on export.

More resources