Configuration
The firmware defines which settings exist, their types, units, defaults and validation; rohi discovers them from the device, so a setting of a new firmware module appears without changing the tool. How a firmware defines a section is in Config.
rohi config works on the sections the device lists. The configuration of a section is its effective
configuration: the defaults of the firmware plus the overrides you set explicitly. Fields are typed and described by
the device; values are parsed against that description (true/false, numbers, text, hex for bytes, a variant name for
enums) and checked again by the device.
rohi config # sections and their versions
rohi config connectivity # the effective values; secrets are shown as <redacted>
rohi config connectivity --help # fields: type, unit, allowed values (from the device)
rohi config connectivity host=ingress.example.org port=443 enabled=true
rohi config connectivity interval_s=120 --dry-run
rohi config wifi ssid=office password=- # secret from stdin or a hidden prompt
rohi config wifi ssid=none # disable Wi-Fi (no ssid is the default)
rohi config wifi scan_timeout=null # remove the override: back to the firmware default
rohi config wifi password=none # explicit "no value": an open network
rohi config reset connectivity # remove every override of the section
A name that is also a command (reset, export, diff, apply) is not a valid section name. rohi config --help
and rohi action --help need no device; rohi config SECTION --help connects and is generated from the schema.
$ rohi config wifi
ssid office
password <redacted>
scan_timeout 20000 ms
connect_timeout 30000 ms
backoff_initial 1000 ms
backoff_max 60000 ms
link_refresh 10000 ms
An optional field without a value shows as <unset> when it follows a default that has none, and as <none> when you
set it to no value explicitly. After a change rohi reads the section
again and prints what the device reports, so the output is always the effective result.
Format the whole configuration storage of the device with the dedicated rohi factory-reset command (not a config
subcommand): every section, the device identity and anything else stored in the partition is erased, including section
versions, and a damaged storage is repaired. The device is unprovisioned afterwards; the command then asks it to reboot (rohi reboot).
rohi factory-reset # asks for confirmation
rohi factory-reset --yes
$ rohi config connectivity --help
Usage:
rohi config connectivity [FIELD=VALUE...]
Fields:
enabled bool
node_id u64
interval_s u64 s
host string
port u64
Special values:
FIELD=null clear the override and use the field default
...
- Effective = defaults + overrides. A field you never set keeps following the firmware default, also after a firmware update changes it; a field you set stays as you set it.
FIELD=VALUEis a patch of overrides: fields that are not mentioned are not touched. All mutations of one command are applied to a copy, the complete effective result is validated on the device and stored atomically: if anything is invalid, nothing is stored.--dry-runvalidates without storing.- The result of every change is validated as
defaults + overrides; an unconfigured or disabled section (nossidmeans Wi-Fi is disabled, no location, connectivity disabled) is a valid state. Fields that belong together are therefore set in one command:rohi config connectivity enabled=truealone fails until thehostis set,rohi config connectivity enabled=true host=ingress.example.orgworks. - Three states for a nullable field.
FIELD=valueis an explicit value,FIELD=noneis an explicit “no value” (rohi config wifi password=noneconfigures an open network,rohi config time server2=nonedisables the second NTP server) andFIELD=nullremoves the override, so the field follows the current firmware default again (rohi config time server2=nullrestores the firmware’s second server).noneis only valid for fields that--helpmarksnullable;rohirefuses it for any other field before contacting the device.FIELD=nullworks for any writable field, required or not. - Secrets (
secretin--help) are never returned and never part of exports. PutNAME=-instead of a value; the tool refuses a secret on the command line.password=noneandpassword=nullare allowed: they carry no secret. - Reset removes every override of the section, so the firmware defaults apply again.
- A change that needs a restart says so (
A restart is required for the change to take effect).
Configuration files
A file describes your overrides, not the defaults:
rohi config export config.json # or: rohi config export > config.json
rohi config diff config.json # what applying would change
rohi config apply config.json --dry-run # validate everything, change nothing
rohi config apply config.json
A file maps section names to their fields:
{
"wifi": { "ssid": "office" },
"connectivity": { "enabled": true, "interval_s": 120, "host": "ingress.example.org", "port": 443 },
"location": { "latitude": 52.52, "longitude": 13.405 }
}
| In the file | Effect |
|---|---|
"field": value | set the override (even if the value equals the default) |
"field": null | remove the override (follow the firmware default) |
"field": {"none": true} | explicit “no value” of a nullable field (distinct from null) |
| field missing | left unchanged |
"section": null | reset the section (remove every override) |
- Export writes the explicit overrides only, never materialized defaults, so a later firmware default still
reaches fields you did not override. A nullable field you set to no value is written as
{"none": true}. Sections without overrides are omitted; secrets and read-only fields are left out, so applying an exported file never clears or overwrites a secret. - A file cannot contain secrets:
config applyrefuses them and points atconfig SECTION NAME=-. diffcompares the file with your overrides on the device and prints~changes,+new overrides,-removed overrides andreset section; nothing is changed. Fields that already match are not listed.applychecks the whole file against the schemas first (unknown section, unknown or read-only field, wrong type fail with no change), then has the device validate every section with a dry run, shows the changes and asks for confirmation unless--yes, then applies the sections one by one. Each section is atomic; applying several is not atomic as a whole. If the device fails in between, the error lists the sections that were applied and the ones that were not. Every section is applied against the revision its diff was computed from, so a section that another client changed after the diff was shown fails with a conflict instead of being overwritten.- The backup of earlier versions (
rohi backup) is replaced byexport/apply; an old backup file is not understood.