API Module
The api/ module is a standalone Go library for communicating with the z13ctl
daemon from external tools — GUI frontends, Decky plugins, shell integrations,
or anything else that wants to control z13ctl programmatically.
Import
The module is deliberately stdlib-only (no third-party dependencies) so that integrations can pull it in without inheriting the CLI's dependency tree.
It is a separate Go module at ./api with its own go.mod. If you are working
on both the main binary and the API library simultaneously, create a
go.work file:
Connection model
All Send* functions open a fresh Unix socket connection to the daemon, send
one JSON request, read one JSON response, and close the connection. This is
intentionally simple and stateless.
If the daemon is not running (connection refused), every Send* function returns
(false, nil) — the first return value (handled bool) signals whether the
daemon was reached. Callers can use this to decide whether to fall back to direct
hardware access.
handled, err := api.SendApply("", "FF0000", "000000", "static", "normal", 3)
if !handled {
// daemon not running; do your own HID access here
}
Subscribe follows the same pattern but holds the connection open to receive a
stream of events.
Socket path
Examples
Apply lighting:
// Static cyan at full brightness on all devices
handled, err := api.SendApply("", "00FFFF", "000000", "static", "normal", 3)
// Breathe between red and blue on the keyboard only
handled, err := api.SendApply("keyboard", "FF0000", "0000FF", "breathe", "slow", 3)
System settings:
// Battery limit
handled, limit, err := api.SendBatteryLimitGet()
handled, err := api.SendBatteryLimitSet(80)
// Performance profile
handled, profile, err := api.SendProfileGet()
handled, err := api.SendProfileSet("performance")
// Boot sound and panel overdrive
handled, err := api.SendBootSoundSet(0)
handled, err := api.SendPanelOverdriveSet(1)
// Fan curves (applied to both fans simultaneously)
handled, value, err := api.SendFanCurveGet()
handled, err := api.SendFanCurveSet("48:2,53:22,57:30,60:43,63:56,65:68,70:89,76:102")
handled, err := api.SendFanCurveReset()
// TDP (PPT power limits)
handled, value, err := api.SendTdpGet()
handled, err := api.SendTdpSet("50", "", "", "", false) // all PPTs to 50W
handled, err := api.SendTdpReset()
// Undervolt (Curve Optimizer — requires ryzen_smu kernel module)
handled, value, err := api.SendUndervoltGet()
handled, err := api.SendUndervoltSet("-20") // CPU CO -20
handled, err := api.SendUndervoltReset()
Full state snapshot (for GUI initialization):
handled, state, err := api.SendGetState()
if handled && err == nil {
fmt.Println("lighting mode:", state.Lighting.Mode)
fmt.Println("profile:", state.Profile)
fmt.Println("battery limit:", state.Battery)
fmt.Println("fan curve:", state.FanCurve)
fmt.Println("tdp:", state.TDP)
fmt.Println("undervolt:", state.Undervolt)
fmt.Println("undervolt available:", state.UndervoltAvailable)
fmt.Println("APU temp:", state.Temperature, "°C")
fmt.Println("fan RPM:", state.FanRPM)
}
Subscribe to Armoury Crate button events:
ch, cancel, err := api.Subscribe([]string{"gui-toggle"})
if ch == nil {
// daemon not running
}
defer cancel()
for event := range ch {
fmt.Println("received:", event)
}
Full API reference
See the API Reference page for full documentation of all exported types and functions.