1 Modules
Vincent S. edited this page 2026-06-28 01:08:01 +02:00

Modules

A module is a data source that the daemon polls on its own interval and caches. Each implements the Module { kind, capabilities, collect } contract from modules/mod.rs (see Architecture).

Built-in modules

Module Source Interval Notes
system /proc/stat, /proc/meminfo, sysfs hwmon 2s always compiled
gpu NVML (libnvidia-ml) 5s nvidia feature
audio wpctl / pactl subprocesses 2s always compiled
media D-Bus MPRIS via zbus 5s media feature
network /sys/class/net 5s always compiled
storage statvfs + sysfs hwmon 30s always compiled
llama HTTP localhost:8080/metrics 2s always compiled
ytmd YTMD companion HTTP + Socket.IO 5s always compiled
hyprland Hyprland IPC 1s always compiled

ModuleOutput

collect() returns a ModuleOutput:

  • waybar fields — text, tooltip, class, percentage
  • stale: bool
  • alert_data: HashMap<String, f64> — structured numerics consumed by the alert engine and state-transition logic
  • meta: HashMap<String, String> — structured strings (artist, title, album, sink, …) consumed by the state-transition logic

State transitions and alerts read alert_data / meta, never the human-facing tooltip. If you want a module's changes logged to the journal, populate those maps.

Adding a module

Adding a module means touching all of these (they must stay in sync):

  1. ModuleKind (alfred-core::types) — add the variant, and update its Display / FromStr / all(). The enum is dep-free and never feature-gated.
  2. Config struct — add the per-module config in alfred-core::config (#[serde(deny_unknown_fields)]; update Config::validate()).
  3. Spawn macro — add the spawn_if_enabled! line in modules/mod.rs.
  4. Control actions live in two places that must match:
    • the module's capabilities() — what the daemon registers and serves at /modules (i.e. what alfred control accepts),
    • the static table in client/list.rs::builtin_metadata — what alfred list prints without a running daemon.

Optional-dependency modules (Cargo feature)

If a module's only heavy/platform-specific dependency is self-contained to its own file, give it a default-on Cargo feature that gates both dep:<crate> and pub mod <module>; + its spawn_if_enabled! line — see nvidia (gpu / nvml-wrapper) and media (zbus).

Because ModuleKind is never gated, a compiled-out module just never spawns — its /control handler and alfred list row still exist.

Blocking work (subprocess calls, sysfs/statvfs, sqlite, NVML) belongs in tokio::task::spawn_blocking, never inline in an async collect().