Skip to main content

Manifest Reference

manifest.toml describes your driver to core. It is the only required file in a package.

The package

A .junodrv is a zip:

manifest.toml required
driver.wasm } exactly one, matching [driver] runtime
driver.py }
commands.toml }
docs/README.md shown in the UI's driver pane — markdown
icons/128.png optional; 128 / 256 / 512
languages/*.po optional

[driver]

[driver]
id = "sony.bravia.ip" # globally unique, reverse-dotted, permanent
name = "Sony Bravia (IP)"
manufacturer = "Sony"
version = "1.2.0" # semver
runtime = "wasm" # wasm | python | declarative
api = 1 # host ABI version
min_core = "0.4.0" # optional

id is permanent. Changing it orphans every existing binding; changing version is how you ship an update.


[[proxy]]

Which device classes you implement. One block per instance — an eight-relay board has eight, a TV with a built-in streamer has two of different types.

[[proxy]]
id = 1
type = "tv"
name = "Living Room TV" # optional; defaults to the driver name
primary = true # optional; the one the UI leads with
capabilities = { has_discrete_power = true, has_discrete_input = true, warmup_ms = 4000 }

id is yours, stable forever, and referenced by control, connection, and every command callback. Start at 1.

capabilities is the important field. Every key must exist in that proxy's contract — see the Proxy Reference — and anything you omit takes the documented default. Core computes your callable command set from this and nothing downstream can call what you did not declare.


control

Control connections you consume. See Connections.

[[control]]
id = 1
kind = "serial"
name = "Projector RS-232"
required = true
FieldRequiredMeaning
idrequiredYours, stable across versions
kindrequiredrelay · contact · ir_out · serial
namerequiredWhat the installer sees. Name the function, not the wire.
requireddefault truefalse means the driver runs without it
proxyoptionalWhich of your proxy bindings it belongs to

[[transport]]

Network only — every other transport is a control connection.

[[transport]]
kind = "tcp" # tcp | udp | http
port = 20060
discovery = "auto" # auto | manual
keepalive = true # reconnect and hold the socket open

connection

Signal connections — the AV graph. See Connections.

[[connection]]
id = 1001
proxy = 1
dir = "consumer"
class = "HDMI"
name = "HDMI 1"

[[connection]]
id = 4001
proxy = 1
dir = "provider"
class = "STEREO"
name = "Zone 2"
FieldRequiredMeaning
idrequiredYours. Passed to set_input, reported by input_changed.
proxyrequiredWhich of your proxy bindings owns this jack
dirrequiredconsumer (input) or provider (output)
classrequiredSignal class
namerequiredThe label silkscreened on the back panel

property

Installer-editable settings, shown in the UI's device pane and readable from your driver with host.get_property(name).

[[property]]
name = "PSK"
type = "password"
tooltip = "Pre-shared key from the TV's IP Control menu"

[[property]]
name = "Poll rate"
type = "ranged_int"
min = 1
max = 300
default = 10
unit = "s"

[[property]]
name = "Zone"
type = "list"
values = ["Main", "Zone 2", "Zone 3"]
default = "Main"

Types: string · password · int · ranged_int · float · bool · list · device_selector · color · label.

password values are stored encrypted and are never returned by the API, logged, or included in a project export.

Your driver is called back on on_property_changed(name) when one is edited. Do not cache a property across that callback.


[discovery]

How core recognises your device on the network. Every matcher is optional; any hit offers the driver to the installer as a one-click bind.

[discovery]
mdns = ["_bravia._tcp", "_airplay._tcp"]
ssdp = ["urn:schemas-sony-com:service:ScalarWebAPI:1"]
sddp = ["Sony:Display"]
mac_oui = ["FC:F1:52", "54:42:49"]
http = [{ path = "/sony/system", contains = "getSystemInformation" }]

An unmatched discovery still shows up in the UI's Discovery list — that list is how installers find out a driver does not exist yet.


Validating

junod driver validate ./my-driver

Checks the manifest against every proxy contract: unknown capabilities, type mismatches, duplicate ids, connections pointing at proxies that do not exist, and required files missing for the declared runtime. Run it before you zip.