Merge pull request #1 from S4NKALP/dev

This commit was merged in pull request #1.
This commit is contained in:
sankalp authored and GitHub committed 2026-03-20 08:39:01 +05:45
commit 2733215d9f
9 files changed
+651 -2

No files matched your search

Generated
+162
View File
@@ -61,6 +61,22 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "cc"
version = "1.2.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "clap"
version = "4.5.60"
@@ -107,6 +123,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "env_filter"
version = "1.0.0"
@@ -130,6 +152,22 @@ dependencies = [
"log",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "heck"
version = "0.5.0"
@@ -182,8 +220,22 @@ dependencies = [
"regex",
"serde",
"serde_json",
"wayland-client",
"wayland-protocols-wlr",
]
[[package]]
name = "libc"
version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "log"
version = "0.4.29"
@@ -202,6 +254,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -226,6 +284,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.45"
@@ -264,6 +331,19 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "serde"
version = "1.0.228"
@@ -307,6 +387,18 @@ dependencies = [
"zmij",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "strsim"
version = "0.11.1"
@@ -336,6 +428,76 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wayland-backend"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406"
dependencies = [
"cc",
"downcast-rs",
"rustix",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3"
dependencies = [
"bitflags",
"rustix",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17"
dependencies = [
"pkg-config",
]
[[package]]
name = "windows-link"
version = "0.2.1"
+3 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "lapctl"
version = "0.1.0"
version = "0.2.0"
edition = "2024"
[dependencies]
@@ -10,3 +10,5 @@ serde_json = "1.0"
log = "0.4"
env_logger = "0.11"
regex = "1.10"
wayland-client = "0.31"
wayland-protocols-wlr = { version = "0.3", features = ["client"] }
+11 -1
View File
@@ -24,6 +24,7 @@ Built with performance and simplicity in mind, it talks directly to your system'
- **Battery Health**: Modern batteries hate being at 100% all the time. Set custom charge limits (like 80%) to significantly extend your battery's lifespan.
- **Power Tuning**: Switch through performance profiles or set hard CPU power (TDP) limits in Watts to keep things cool or let them loose.
- **Intelligent Cooling**: Force your fans into Performance, Balanced, or Quiet modes (supporting ASUS and Lenovo laptops).
- **Display Refresh Rate**: Easily query available refresh rates and change your active display's Hz on-the-fly (100% native Rust Wayland implementation using `zwlr_output_manager_v1` for wlroots compositors like Sway and Hyprland).
- **Touchpad Toggle**: Quickly enable or disable your touchpad from the terminal when using an external mouse.
- **Sleep Inhibitor**: Running a long compile or a critical download? Use the inhibitor to stop your laptop from falling asleep mid task.
- **Instant Status**: Get a bird's eye view of your hardware state, battery health, and current limits with one simple command.
@@ -47,7 +48,11 @@ cargo install --path .
#### Requirements
- **systemd**: For sleep inhibitor (`systemd-inhibit`)
- **X11/NVIDIA Tools**: `xrandr`, `nvidia-settings` for GPU management
- **GPU Switching (Optional)**: `xrandr` and `nvidia-settings` are strictly required **ONLY** when using the `lapctl gpu` command on X11 (to route proprietary NVIDIA Optimus drivers).
- **Wayland Display**: Built entirely natively using `wayland-client` and `wayland-protocols-wlr` (no `wlr-randr` required!)
#### Limitations
- **GNOME / KDE Plasma (Wayland)**: The display refresh rate feature relies heavily on the `zwlr_output_manager_v1` protocol. This protocol is exclusive to wlroots-based compositors (like Sway and Hyprland). GNOME and KDE use their own disparate internal display protocols, meaning this feature will **not work** out-of-the-box on those Desktop Environments.
---
@@ -58,6 +63,7 @@ cargo install --path .
lapctl gpu integrated # Max battery
lapctl gpu hybrid # Best of both worlds
lapctl gpu nvidia # High performance
lapctl gpu run steam # Run 'steam' on dGPU directly while in Hybrid mode
# Prolong battery life
lapctl battery limit 80
@@ -76,6 +82,10 @@ lapctl cooling performance
lapctl touchpad disable
lapctl touchpad enable
# Manage your display refresh rate
lapctl display rates
lapctl display set-rate 144
# Keep it awake
lapctl inhibit --daemon # Run in background
lapctl inhibit -- why "Critical update" ./long-task.sh
+22
View File
@@ -37,6 +37,11 @@ pub enum Commands {
#[command(subcommand)]
command: CoolingCommands,
},
/// Display management commands
Display {
#[command(subcommand)]
command: DisplayCommands,
},
/// Hardware status
Status,
/// Install udev rules for rootless operation
@@ -105,6 +110,12 @@ pub enum GpuCommands {
CacheDelete,
/// Show cache created by lapctl
CacheQuery,
/// Run an application on the discrete GPU (Requires Hybrid Mode)
Run {
/// The command and arguments to execute
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
command: Vec<String>,
},
}
#[derive(Subcommand, Debug)]
@@ -144,3 +155,14 @@ pub enum TouchpadCommands {
/// Disable the touchpad
Disable,
}
#[derive(Subcommand, Debug)]
pub enum DisplayCommands {
/// Show available and active refresh rates
Rates,
/// Set the display refresh rate
SetRate {
/// Target refresh rate in Hz
rate: f32,
},
}
+402
View File
@@ -0,0 +1,402 @@
use crate::cli::DisplayCommands;
use log::error;
use std::collections::HashMap;
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle, protocol::wl_registry};
use wayland_protocols_wlr::output_management::v1::client::{
zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1,
zwlr_output_configuration_v1::{self, ZwlrOutputConfigurationV1},
zwlr_output_head_v1::{self, ZwlrOutputHeadV1},
zwlr_output_manager_v1::{self, ZwlrOutputManagerV1},
zwlr_output_mode_v1::{self, ZwlrOutputModeV1},
};
pub fn execute(command: &DisplayCommands) {
match command {
DisplayCommands::Rates => {
show_refresh_rates();
}
DisplayCommands::SetRate { rate } => {
set_refresh_rate(*rate);
}
}
}
// State Structs
#[derive(Debug, Default, Clone)]
pub struct WlMode {
pub width: i32,
pub height: i32,
pub refresh: i32,
pub preferred: bool,
}
#[derive(Debug, Clone)]
pub struct WlHead {
pub name: String,
pub description: String,
pub make: String,
pub model: String,
pub enabled: bool,
pub current_mode: Option<ZwlrOutputModeV1>,
pub x: i32,
pub y: i32,
pub scale: f64,
pub transform: wayland_client::protocol::wl_output::Transform,
}
impl Default for WlHead {
fn default() -> Self {
Self {
name: String::new(),
description: String::new(),
make: String::new(),
model: String::new(),
enabled: false,
current_mode: None,
x: 0,
y: 0,
scale: 1.0,
transform: wayland_client::protocol::wl_output::Transform::Normal,
}
}
}
pub struct AppState {
pub manager: Option<ZwlrOutputManagerV1>,
pub heads: HashMap<ZwlrOutputHeadV1, WlHead>,
pub modes: HashMap<ZwlrOutputModeV1, (ZwlrOutputHeadV1, WlMode)>,
pub done: bool,
pub config_success: Option<bool>,
}
impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
let wl_registry::Event::Global {
name,
interface,
version,
} = event
else {
return;
};
if interface == "zwlr_output_manager_v1" {
let manager = registry.bind::<ZwlrOutputManagerV1, _, _>(name, version, qh, ());
state.manager = Some(manager);
}
}
}
impl Dispatch<ZwlrOutputManagerV1, ()> for AppState {
fn event(
state: &mut Self,
_: &ZwlrOutputManagerV1,
event: zwlr_output_manager_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
match event {
zwlr_output_manager_v1::Event::Head { head } => {
state.heads.insert(head, WlHead::default());
}
zwlr_output_manager_v1::Event::Done { .. } => {
state.done = true;
}
_ => {}
}
}
fn event_created_child(
opcode: u16,
qh: &QueueHandle<Self>,
) -> std::sync::Arc<dyn wayland_client::backend::ObjectData> {
match opcode {
0 => qh.make_data::<ZwlrOutputHeadV1, ()>(()),
_ => panic!(
"Missing event_created_child specialization for opcode {}",
opcode
),
}
}
}
impl Dispatch<ZwlrOutputHeadV1, ()> for AppState {
fn event(
state: &mut Self,
head: &ZwlrOutputHeadV1,
event: zwlr_output_head_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let Some(h) = state.heads.get_mut(head) {
match event {
zwlr_output_head_v1::Event::Name { name } => h.name = name,
zwlr_output_head_v1::Event::Description { description } => {
h.description = description
}
zwlr_output_head_v1::Event::Make { make } => h.make = make,
zwlr_output_head_v1::Event::Model { model } => h.model = model,
// Mode event gives us a new mode object!
zwlr_output_head_v1::Event::Mode { mode } => {
state.modes.insert(mode, (head.clone(), WlMode::default()));
}
zwlr_output_head_v1::Event::Enabled { enabled } => h.enabled = enabled != 0,
zwlr_output_head_v1::Event::CurrentMode { mode } => h.current_mode = Some(mode),
zwlr_output_head_v1::Event::Position { x, y } => {
h.x = x;
h.y = y;
}
zwlr_output_head_v1::Event::Scale { scale } => h.scale = scale,
zwlr_output_head_v1::Event::Transform {
transform: wayland_client::WEnum::Value(t),
} => {
h.transform = t;
}
_ => {}
}
}
}
fn event_created_child(
opcode: u16,
qh: &QueueHandle<Self>,
) -> std::sync::Arc<dyn wayland_client::backend::ObjectData> {
match opcode {
3 => qh.make_data::<ZwlrOutputModeV1, ()>(()),
_ => panic!(
"Missing event_created_child specialization for opcode {} on head",
opcode
),
}
}
}
impl Dispatch<ZwlrOutputModeV1, ()> for AppState {
fn event(
state: &mut Self,
mode: &ZwlrOutputModeV1,
event: zwlr_output_mode_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let Some((head_handle, mut wl_mode)) = state.modes.remove(mode) {
match event {
zwlr_output_mode_v1::Event::Size { width, height } => {
wl_mode.width = width;
wl_mode.height = height;
}
zwlr_output_mode_v1::Event::Refresh { refresh } => {
wl_mode.refresh = refresh;
}
zwlr_output_mode_v1::Event::Preferred => {
wl_mode.preferred = true;
}
_ => {}
}
state
.modes
.insert(mode.clone(), (head_handle.clone(), wl_mode));
}
}
}
impl Dispatch<ZwlrOutputConfigurationV1, ()> for AppState {
fn event(
state: &mut Self,
_: &ZwlrOutputConfigurationV1,
event: zwlr_output_configuration_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
match event {
zwlr_output_configuration_v1::Event::Succeeded => state.config_success = Some(true),
zwlr_output_configuration_v1::Event::Failed => state.config_success = Some(false),
zwlr_output_configuration_v1::Event::Cancelled => state.config_success = Some(false),
_ => {}
}
}
}
impl Dispatch<ZwlrOutputConfigurationHeadV1, ()> for AppState {
fn event(
_: &mut Self,
_: &ZwlrOutputConfigurationHeadV1,
_: wayland_protocols_wlr::output_management::v1::client::zwlr_output_configuration_head_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
fn fetch_state() -> Option<(Connection, AppState, wayland_client::EventQueue<AppState>)> {
let conn = Connection::connect_to_env().ok()?;
let mut event_queue = conn.new_event_queue();
let qh = event_queue.handle();
let display = conn.display();
let mut state = AppState {
manager: None,
heads: HashMap::new(),
modes: HashMap::new(),
done: false,
config_success: None,
};
display.get_registry(&qh, ());
event_queue.roundtrip(&mut state).ok()?;
if state.manager.is_none() {
error!("Your compositor does not support zwlr_output_manager_v1.");
return None;
}
// Two roundtrips to fetch heads then modes
event_queue.roundtrip(&mut state).ok()?;
event_queue.roundtrip(&mut state).ok()?;
Some((conn, state, event_queue))
}
pub fn get_active_display_info() -> Vec<String> {
let mut infos = Vec::new();
if let Some((_, state, _)) = fetch_state() {
for (_, head) in state.heads.iter() {
if !head.enabled {
continue;
}
let Some(current_mode_handle) = &head.current_mode else {
continue;
};
let Some((_, mode)) = state.modes.get(current_mode_handle) else {
continue;
};
let hz = (mode.refresh as f64) / 1000.0;
infos.push(format!(
"{} ({}): {}x{} px, {:.3} Hz",
head.name, head.make, mode.width, mode.height, hz
));
}
}
infos
}
fn show_refresh_rates() {
if let Some((_, state, _)) = fetch_state() {
for (head_handle, head) in state.heads.iter() {
println!("{} \"{}\"", head.name, head.description);
println!(" Make: {}", head.make);
println!(" Model: {}", head.model);
println!(" Enabled: {}", if head.enabled { "yes" } else { "no" });
println!(" Modes:");
// Gather modes for this head
let mut head_modes: Vec<(&ZwlrOutputModeV1, &WlMode)> = state
.modes
.iter()
.filter(|(_, (mh, _))| mh == head_handle)
.map(|(mode_handle, (_, mode))| (mode_handle, mode))
.collect();
// Sort by resolution descending, then refresh rate descending
head_modes.sort_by(|a, b| {
b.1.width
.cmp(&a.1.width)
.then(b.1.height.cmp(&a.1.height))
.then(b.1.refresh.cmp(&a.1.refresh))
});
// Deduplicate modes based on properties
let mut seen = std::collections::HashSet::new();
for (mode_handle, mode) in head_modes {
if mode.width != 0 && mode.height != 0 && mode.refresh != 0 {
let key = format!("{}x{}@{}", mode.width, mode.height, mode.refresh);
let is_current = head.current_mode.as_ref() == Some(mode_handle);
let is_preferred = mode.preferred;
// Always show current/preferred, otherwise deduplicate identical ones
if seen.insert(key) || is_current || is_preferred {
let hz = (mode.refresh as f64) / 1000.0;
let mut markers = Vec::new();
if is_preferred {
markers.push("preferred");
}
if is_current {
markers.push("current");
}
let marker_str = if markers.is_empty() {
String::new()
} else {
format!(" ({})", markers.join(", "))
};
println!(
" {}x{} px, {:.6} Hz{}",
mode.width, mode.height, hz, marker_str
);
}
}
}
}
} else {
error!("Native Wayland configuration failed. Are you on a wlroots compositor?");
}
}
fn set_refresh_rate(rate: f32) {
let Some((_conn, mut state, mut eq)) = fetch_state() else {
return;
};
let Some(manager) = state.manager.clone() else {
return;
};
let qh = eq.handle();
let config = manager.create_configuration(manager.version(), &qh, ());
if let Some((head_handle, head_info)) = state.heads.iter().find(|(_, h)| h.enabled) {
let target_mhz = (rate * 1000.0) as i32;
for (mode_handle, (mh, mode_info)) in state.modes.iter() {
if mh == head_handle {
// fuzzy match the mHz
if (mode_info.refresh - target_mhz).abs() < 5000 {
let head_config = config.enable_head(head_handle, &qh, ());
head_config.set_mode(mode_handle);
head_config.set_position(head_info.x, head_info.y);
head_config.set_scale(head_info.scale);
head_config.set_transform(head_info.transform);
config.apply();
state.config_success = None;
while state.config_success.is_none() {
if eq.blocking_dispatch(&mut state).is_err() {
break;
}
}
if state.config_success == Some(true) {
println!("[SUCCESS] Applied Wayland output configuration!");
} else {
error!("[FAILED] Compositor rejected the output configuration request.");
}
return;
}
}
}
}
}
+38
View File
@@ -485,6 +485,43 @@ fn switch_nvidia(
println!("Please reboot your computer for changes to take effect!");
}
fn run_on_dgpu(command: &[String]) {
if command.is_empty() {
error!("No command provided to run.");
return;
}
if get_current_mode() != "hybrid" {
log::warn!(
"Running on dGPU is typically only effective in Hybrid mode. \
Your current mode is '{}'. The application may fail to start or \
not use the NVIDIA GPU.",
get_current_mode()
);
}
log::info!("Launching '{}' on the discrete GPU...", command.join(" "));
let mut child = Command::new(&command[0]);
if command.len() > 1 {
child.args(&command[1..]);
}
// Set PRIME offload variables natively
child.env("__NV_PRIME_RENDER_OFFLOAD", "1");
child.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
child.env("__VK_LAYER_NV_optimus", "NVIDIA_only");
match child.status() {
Ok(status) => {
log::info!("Application exited with: {}", status);
}
Err(e) => {
error!("Failed to launch application: {}", e);
}
}
}
pub fn execute(cmd: &GpuCommands) {
match cmd {
GpuCommands::Query => {
@@ -551,5 +588,6 @@ pub fn execute(cmd: &GpuCommands) {
*use_nvidia_current,
*wayland,
),
GpuCommands::Run { command } => run_on_dgpu(command),
}
}
+1
View File
@@ -1,5 +1,6 @@
pub mod battery;
pub mod cooling;
pub mod display;
pub mod gpu;
pub mod inhibit;
pub mod install_rules;
+11
View File
@@ -1,3 +1,4 @@
use crate::commands::display;
use crate::hardware::gpu;
use std::fs;
use std::path::Path;
@@ -179,4 +180,14 @@ pub fn execute() {
}
}
}
// Display Status
let displays = display::get_active_display_info();
if displays.is_empty() {
println!("Display: Unknown / Firmware managed");
} else {
for d in displays {
println!("Display: {}", d);
}
}
}
+1
View File
@@ -26,6 +26,7 @@ fn main() {
Commands::Battery { command } => commands::battery::execute(command),
Commands::Power { command } => commands::power::execute(command),
Commands::Cooling { command } => commands::cooling::execute(command),
Commands::Display { command } => commands::display::execute(command),
Commands::Status => commands::status::execute(),
Commands::InstallRules => commands::install_rules::execute(),
Commands::Touchpad { command } => commands::touchpad::execute(command),