diff --git a/asus-shutdown/src/main.rs b/asus-shutdown/src/main.rs index b52650f8..4073b147 100644 --- a/asus-shutdown/src/main.rs +++ b/asus-shutdown/src/main.rs @@ -152,23 +152,23 @@ async fn acquire_shutdown_inhibitor(manager: &ManagerProxy<'_>) -> Result { - println!("- none"); + info!("- none"); } Ok(actions) => { for action in actions { - println!( + info!( "- {} => {} (path: {})", action.name, action.value, action.path ); } } Err(err) => { - println!("- could not query asusd queue: {err}"); + warn!("- could not query asusd queue: {err}"); } } } diff --git a/asusctl/src/anime_cli.rs b/asusctl/src/anime_cli.rs index ea1dfa68..6bcafb70 100644 --- a/asusctl/src/anime_cli.rs +++ b/asusctl/src/anime_cli.rs @@ -1,6 +1,8 @@ use argh::FromArgs; -use rog_anime::usb::{AnimAwake, AnimBooting, AnimShutdown, AnimSleeping}; +use log::warn; +use rog_anime::usb::{get_anime_type, AnimAwake, AnimBooting, AnimShutdown, AnimSleeping}; use rog_anime::AnimeType; +use rog_dbus::find_iface_blocking; #[derive(FromArgs, Debug)] #[argh(subcommand, name = "anime", description = "anime commands")] @@ -30,8 +32,6 @@ pub struct AnimeCommand { pub off_when_suspended: Option, #[argh(option, description = "turn the anime off when the lid is closed")] pub off_when_lid_closed: Option, - #[argh(option, description = "off with his head!!!")] - pub off_with_his_head: Option, #[argh(subcommand)] pub command: Option, } @@ -149,3 +149,228 @@ pub struct AnimeGifDiagonal { )] pub loops: u32, } + +pub fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box> { + if cmd.command.is_none() + && cmd.enable_display.is_none() + && cmd.enable_powersave_anim.is_none() + && cmd.brightness.is_none() + && cmd.off_when_lid_closed.is_none() + && cmd.off_when_suspended.is_none() + && cmd.off_when_unplugged.is_none() + && !cmd.clear + { + warn!("Missing arg or command; run 'asusctl anime --help' for usage"); + return Ok(()); + } + + if let Some(action) = cmd.command.as_ref() { + match action { + AnimeActions::Image(image) if image.path.is_empty() => { + warn!("Missing arg or command; run 'asusctl anime image --help' for usage"); + return Ok(()); + } + AnimeActions::Image(image) => verify_brightness(image.bright)?, + AnimeActions::PixelImage(image) if image.path.is_empty() => { + warn!("Missing arg or command; run 'asusctl anime pixel-image --help' for usage"); + return Ok(()); + } + AnimeActions::PixelImage(image) => verify_brightness(image.bright)?, + AnimeActions::Gif(gif) if gif.path.is_empty() => { + warn!("Missing arg or command; run 'asusctl anime gif --help' for usage"); + return Ok(()); + } + AnimeActions::Gif(gif) => verify_brightness(gif.bright)?, + AnimeActions::PixelGif(gif) if gif.path.is_empty() => { + warn!("Missing arg or command; run 'asusctl anime pixel-gif --help' for usage"); + return Ok(()); + } + AnimeActions::PixelGif(gif) => verify_brightness(gif.bright)?, + AnimeActions::SetBuiltins(builtins) if builtins.set.is_none() => { + warn!("Missing arg; run 'asusctl anime set-builtins --help' for usage"); + return Ok(()); + } + _ => {} + } + } + + let animes = + find_iface_blocking::("xyz.ljones.Anime")?; + + let mut anime_type = get_anime_type(); + if let Some(model) = cmd.override_type { + anime_type = model; + } else if let AnimeType::Unsupported = anime_type { + warn!("Anime display type is Unsupported; consider specifying --override-type"); + } + + for proxy in animes { + if let Some(enable) = cmd.enable_display { + proxy.set_enable_display(enable)?; + } + if let Some(enable) = cmd.enable_powersave_anim { + proxy.set_builtins_enabled(enable)?; + } + if let Some(bright) = cmd.brightness { + proxy.set_brightness(bright)?; + } + if let Some(enable) = cmd.off_when_lid_closed { + proxy.set_off_when_lid_closed(enable)?; + } + if let Some(enable) = cmd.off_when_suspended { + proxy.set_off_when_suspended(enable)?; + } + if let Some(enable) = cmd.off_when_unplugged { + proxy.set_off_when_unplugged(enable)?; + } + + if cmd.clear { + let data = vec![255u8; anime_type.data_length()]; + let tmp = rog_anime::AnimeDataBuffer::from_vec(anime_type, data)?; + proxy.write(tmp)?; + } + + if let Some(action) = cmd.command.as_ref() { + match action { + AnimeActions::Image(image) => { + let matrix = rog_anime::AnimeImage::from_png( + std::path::Path::new(&image.path), + image.scale, + image.angle, + rog_anime::Vec2::new(image.x_pos, image.y_pos), + image.bright, + anime_type, + )?; + + proxy.write(::try_from(&matrix)?)?; + } + AnimeActions::PixelImage(image) => { + let matrix = rog_anime::AnimeDiagonal::from_png( + std::path::Path::new(&image.path), + None, + image.bright, + anime_type, + )?; + + proxy.write(matrix.into_data_buffer(anime_type)?)?; + } + AnimeActions::Gif(gif) => { + let matrix = rog_anime::AnimeGif::from_gif( + std::path::Path::new(&gif.path), + gif.scale, + gif.angle, + rog_anime::Vec2::new(gif.x_pos, gif.y_pos), + rog_anime::AnimTime::Count(1), + gif.bright, + anime_type, + )?; + + play_gif_animation(&proxy, &matrix, gif.loops)?; + } + AnimeActions::PixelGif(gif) => { + let matrix = rog_anime::AnimeGif::from_diagonal_gif( + std::path::Path::new(&gif.path), + rog_anime::AnimTime::Count(1), + gif.bright, + anime_type, + )?; + + play_gif_animation(&proxy, &matrix, gif.loops)?; + } + AnimeActions::SetBuiltins(builtins) => { + if builtins.set == Some(true) { + proxy.set_builtin_animations(rog_anime::Animations { + boot: builtins.boot, + awake: builtins.awake, + sleep: builtins.sleep, + shutdown: builtins.shutdown, + })?; + } + } + } + } + } + Ok(()) +} + +/// Helper to determine the playback iteration strategy. +/// Returns `None` for infinite loops (`loops == 0`), or `Some(count)` for finite playback. +fn compute_loop_plan(loops: u32) -> Option { + if loops == 0 { + None + } else { + Some(loops) + } +} + +/// Play GIF animation frames. `loops == 0` means infinite playback until interrupted. +fn play_gif_animation( + proxy: &rog_dbus::zbus_anime::AnimeProxyBlocking, + matrix: &rog_anime::AnimeGif, + loops: u32, +) -> Result<(), Box> { + let mut remaining = compute_loop_plan(loops); + + loop { + for frame in matrix.frames() { + proxy.write(frame.frame().clone())?; + std::thread::sleep(frame.delay()); + } + match remaining { + None => continue, + Some(1) => break, + Some(ref mut count) => *count -= 1, + } + } + Ok(()) +} + +fn verify_brightness(brightness: f32) -> Result<(), Box> { + if !(0.0..=1.0).contains(&brightness) { + return Err(format!( + "Brightness must be between 0.0 and 1.0 (inclusive), was {brightness}" + ) + .into()); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_verify_brightness_valid() { + assert!(verify_brightness(0.0).is_ok()); + assert!(verify_brightness(0.5).is_ok()); + assert!(verify_brightness(1.0).is_ok()); + } + + #[test] + fn test_verify_brightness_invalid() { + assert!(verify_brightness(-0.1).is_err()); + assert!(verify_brightness(1.1).is_err()); + assert!(verify_brightness(f32::NAN).is_err()); + assert!(verify_brightness(f32::INFINITY).is_err()); + assert!(verify_brightness(f32::NEG_INFINITY).is_err()); + } + + #[test] + fn test_loop_iteration_count() { + assert_eq!( + compute_loop_plan(0), + None, + "0 loops must plan for infinite playback" + ); + assert_eq!( + compute_loop_plan(1), + Some(1), + "1 loop must plan for 1 iteration" + ); + assert_eq!( + compute_loop_plan(5), + Some(5), + "5 loops must plan for 5 iterations" + ); + } +} diff --git a/asusctl/src/fan_curve_cli.rs b/asusctl/src/fan_curve_cli.rs index 1060cece..b5071f9b 100644 --- a/asusctl/src/fan_curve_cli.rs +++ b/asusctl/src/fan_curve_cli.rs @@ -1,4 +1,5 @@ use argh::FromArgs; +use log::warn; use rog_platform::platform::PlatformProfile; use rog_profiles::fan_curve_set::CurveData; use rog_profiles::FanCurvePU; @@ -42,3 +43,77 @@ pub struct FanCurveCommand { )] pub data: Option, } + +const REQ_MOD_PROFILE_MSG: &str = + "--enable-fan-curves, --enable-fan-curve, --fan, and --data options require --mod-profile"; + +pub fn handle_fan_curve( + conn: &zbus::blocking::Connection, + cmd: &FanCurveCommand, +) -> Result<(), Box> { + if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() { + warn!("Missing arg or command; run 'asusctl fan-curve --help' for usage"); + return Ok(()); + } + + if (cmd.enable_fan_curves.is_some() + || cmd.enable_fan_curve.is_some() + || cmd.fan.is_some() + || cmd.data.is_some()) + && cmd.mod_profile.is_none() + { + warn!("{REQ_MOD_PROFILE_MSG}"); + return Ok(()); + } + + let fan_proxy = rog_dbus::zbus_fan_curves::FanCurvesProxyBlocking::new(conn).map_err(|e| { + warn!("Fan curves unavailable: {e}"); + rog_profiles::error::ProfileError::NotSupported + })?; + + let plat_proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(conn)?; + if cmd.get_enabled { + let profile = plat_proxy.platform_profile()?; + let curves = fan_proxy.fan_curve_data(profile)?; + for curve in curves.iter() { + log::info!("{}", String::from(curve)); + } + } + + if cmd.default { + let active = plat_proxy.platform_profile()?; + fan_proxy.set_curves_to_defaults(active)?; + } + + if let Some(profile) = cmd.mod_profile { + if cmd.enable_fan_curves.is_none() && cmd.enable_fan_curve.is_none() && cmd.data.is_none() { + let data = fan_proxy.fan_curve_data(profile)?; + let ron = + ron::ser::to_string_pretty(&data, ron::ser::PrettyConfig::new().depth_limit(4))?; + log::info!("\nFan curves for {:?}\n\n{}", profile, ron); + } + + if let Some(enabled) = cmd.enable_fan_curves { + fan_proxy.set_fan_curves_enabled(profile, enabled)?; + } + + if let Some(enabled) = cmd.enable_fan_curve { + if let Some(fan) = cmd.fan { + fan_proxy.set_profile_fan_curve_enabled(profile, fan, enabled)?; + } else { + warn!("--enable-fan-curve requires --fan "); + } + } + + if let Some(mut curve) = cmd.data.clone() { + if let Some(fan) = cmd.fan { + curve.set_fan(fan); + fan_proxy.set_fan_curve(profile, curve)?; + } else { + warn!("--data requires --fan "); + } + } + } + + Ok(()) +} diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 534f0ce4..75f778b9 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -1,57 +1,30 @@ -use std::convert::TryFrom; -use std::path::Path; -use std::process::Command; -use std::thread::sleep; - -use anime_cli::{AnimeActions, AnimeCommand}; -use aura_cli::{LedPowerCommand1, LedPowerCommand2}; -use dmi_id::DMIID; -use fan_curve_cli::FanCurveCommand; -use log::{error, info, LevelFilter}; -use rog_anime::usb::get_anime_type; -use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2}; -use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; -use rog_aura::{self, AuraEffect, PowerZones}; -use rog_dbus::asus_armoury::AsusArmouryProxyBlocking; +use log::{error, info}; use rog_dbus::list_iface_blocking; -use rog_dbus::scsi_aura::ScsiAuraProxyBlocking; -use rog_dbus::zbus_anime::AnimeProxyBlocking; -use rog_dbus::zbus_aura::AuraProxyBlocking; -use rog_dbus::zbus_backlight::BacklightProxyBlocking; -use rog_dbus::zbus_fan_curves::FanCurvesProxyBlocking; use rog_dbus::zbus_platform::PlatformProxyBlocking; -use rog_platform::asus_armoury::FirmwareAttributeType; -use rog_platform::platform::{PlatformProfile, Properties}; -use rog_profiles::error::ProfileError; -use rog_scsi::AuraMode; -use ron::ser::PrettyConfig; -use scsi_cli::ScsiCommand; -use zbus::blocking::proxy::ProxyImpl; +use rog_platform::platform::Properties; use zbus::blocking::Connection; use crate::cli_opts::*; -use crate::slash_cli::{ - handle_slash_get, handle_slash_list, handle_slash_set, SlashCommand, SlashSubCommand, +use crate::platform_cli::{ + check_service, handle_armoury_command, handle_backlight, handle_battery, handle_brightness, + handle_info, handle_led_mode, handle_led_power1, handle_led_power2, handle_throttle_profile, + print_info, }; +use crate::slash_cli::{handle_slash_get, handle_slash_list, handle_slash_set, SlashSubCommand}; mod anime_cli; mod aura_cli; mod cli_opts; mod fan_curve_cli; +mod platform_cli; mod scsi_cli; mod slash_cli; mod xgm_led_cli; fn main() { - // Ensure tracing spans are quiet by default unless user overrides - if std::env::var_os("RUST_LOG").is_none() { - std::env::set_var("RUST_LOG", "warn,tracing=error,zbus=error"); - } - let mut logger = env_logger::Builder::new(); - logger - .parse_default_env() - .filter_level(LevelFilter::Info) - .target(env_logger::Target::Stderr) + let env = env_logger::Env::default().default_filter_or("info,tracing=error,zbus=error"); + env_logger::Builder::from_env(env) + .target(env_logger::Target::Stdout) .format_timestamp(None) .init(); @@ -66,7 +39,7 @@ fn main() { }; if let Ok(platform_proxy) = PlatformProxyBlocking::new(&conn).map_err(|e| { check_service("asusd"); - println!("\nError: {e}\n"); + error!("Error: {e}"); print_info(); }) { let asusd_version = match platform_proxy.version() { @@ -82,7 +55,7 @@ fn main() { let self_version = env!("CARGO_PKG_VERSION"); if asusd_version != self_version { - println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); + error!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); return; } @@ -101,7 +74,7 @@ fn main() { } }; - if let Err(err) = do_parsed(&parsed, &supported_interfaces, &supported_properties, conn) { + if let Err(err) = do_parsed(&parsed, &supported_interfaces, &supported_properties, &conn) { print_error_help(&*err, &supported_interfaces, &supported_properties); } } @@ -113,85 +86,17 @@ fn print_error_help( supported_properties: &[Properties], ) { check_service("asusd"); - println!("\nError: {}\n", err); + error!("Error: {err}"); print_info(); - println!(); - println!("Supported interfaces:\n\n{:#?}\n", supported_interfaces); - println!( - "Supported properties on Platform:\n\n{:#?}\n", - supported_properties - ); -} - -fn print_info() { - let dmi = DMIID::new().unwrap_or_default(); - let board_name = dmi.board_name; - let prod_family = dmi.product_family; - println!("Software version: {}", env!("CARGO_PKG_VERSION")); - println!(" Product family: {}", prod_family.trim()); - println!(" Board name: {}", board_name.trim()); -} - -fn check_service(name: &str) -> bool { - if name != "asusd" && !check_systemd_unit_enabled(name) { - println!( - "\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m", - name, name - ); - return true; - } else if !check_systemd_unit_active(name) { - println!( - "\n\x1b[0;31m{} is not running, start it with `systemctl start {}\x1b[0m", - name, name - ); - return true; - } - false -} - -fn find_iface(iface_name: &str) -> Result, Box> -where - T: ProxyImpl<'static> + From>, -{ - let conn = zbus::blocking::Connection::system()?; - let f = zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/")?; - let interfaces = f.get_managed_objects()?; - let mut paths = Vec::new(); - for v in interfaces.iter() { - // let o: Vec = v.1.keys().map(|e| - // e.to_owned()).collect(); println!("{}, {:?}", v.0, o); - for k in v.1.keys() { - if k.as_str() == iface_name { - // println!("Found {iface_name} device at {}, {}", v.0, k); - paths.push(v.0.clone()); - } - } - } - if paths.len() > 1 { - println!("Multiple asusd interfaces devices found"); - } - if !paths.is_empty() { - let mut ctrl = Vec::new(); - paths.sort_by(|a, b| a.cmp(b)); - for path in paths { - ctrl.push( - T::builder(&conn) - .path(path.clone())? - .destination("xyz.ljones.Asusd")? - .build()?, - ); - } - return Ok(ctrl); - } - - Err(format!("Did not find {iface_name}").into()) + info!("Supported interfaces:\n\n{supported_interfaces:#?}\n"); + info!("Supported properties on Platform:\n\n{supported_properties:#?}\n"); } fn do_parsed( parsed: &CliStart, supported_interfaces: &[String], supported_properties: &[Properties], - conn: Connection, + conn: &Connection, ) -> Result<(), Box> { match &parsed.command { CliCommand::Aura(a) => match &a.command { @@ -200,899 +105,23 @@ fn do_parsed( crate::cli_opts::AuraSubCommand::Power(pow) => handle_led_power2(pow)?, }, CliCommand::Brightness(cmd) => handle_brightness(cmd)?, - CliCommand::Profile(cmd) => handle_throttle_profile(&conn, supported_properties, cmd)?, - CliCommand::FanCurve(cmd) => handle_fan_curve(&conn, cmd)?, - CliCommand::Anime(cmd) => handle_anime(cmd)?, - CliCommand::Slash(cmd) => handle_slash(cmd)?, - CliCommand::Scsi(cmd) => handle_scsi(cmd)?, - CliCommand::Armoury(cmd) => handle_armoury_command(cmd, &conn)?, + CliCommand::Profile(cmd) => handle_throttle_profile(conn, supported_properties, cmd)?, + CliCommand::FanCurve(cmd) => fan_curve_cli::handle_fan_curve(conn, cmd)?, + CliCommand::Anime(cmd) => anime_cli::handle_anime(cmd)?, + CliCommand::Slash(cmd) => match &cmd.command { + SlashSubCommand::Get(_) => handle_slash_get(conn)?, + SlashSubCommand::Set(cmd) => handle_slash_set(cmd, conn)?, + SlashSubCommand::List(_) => handle_slash_list(), + }, + CliCommand::Scsi(cmd) => scsi_cli::handle_scsi(cmd)?, + CliCommand::Armoury(cmd) => handle_armoury_command(cmd, conn)?, CliCommand::Backlight(cmd) => handle_backlight(cmd)?, - CliCommand::Battery(cmd) => handle_battery(cmd, &conn)?, - CliCommand::XgmLed(cmd) => xgm_led_cli::handle_xgm_led(&cmd.command)?, + CliCommand::Battery(cmd) => handle_battery(cmd, conn)?, + CliCommand::XgmLed(cmd) => xgm_led_cli::handle_xgm_led(conn, &cmd.command)?, CliCommand::Info(info_opt) => { - handle_info(info_opt, supported_interfaces, supported_properties)? - } - } - - Ok(()) -} - -fn handle_battery( - cmd: &BatteryCommand, - conn: &Connection, -) -> Result<(), Box> { - match &cmd.command { - BatterySubCommand::Limit(l) => { - let proxy = PlatformProxyBlocking::new(conn)?; - proxy.set_charge_control_end_threshold(l.limit)?; - } - BatterySubCommand::OneShot(o) => { - let proxy = PlatformProxyBlocking::new(conn)?; - if let Some(p) = o.percent { - proxy.set_charge_control_end_threshold(p)?; - } - proxy.one_shot_full_charge()?; - } - BatterySubCommand::Info(_) => { - let proxy = PlatformProxyBlocking::new(conn)?; - let limit = proxy.charge_control_end_threshold()?; - println!("Current battery charge limit: {}%", limit); - } - } - - Ok(()) -} - -fn handle_info( - info_opt: &InfoCommand, - supported_interfaces: &[String], - supported_properties: &[Properties], -) -> Result<(), Box> { - println!("asusctl v{}", env!("CARGO_PKG_VERSION")); - println!(); - print_info(); - println!(); - - if info_opt.show_supported { - println!("Supported Core Functions:\n{:#?}", supported_interfaces); - println!( - "Supported Platform Properties:\n{:#?}", - supported_properties - ); - if let Ok(aura) = find_iface::("xyz.ljones.Aura") { - // TODO: multiple RGB check - if let Some(first_aura) = aura.first() { - let bright = first_aura.supported_brightness()?; - let modes = first_aura.supported_basic_modes()?; - let zones = first_aura.supported_basic_zones()?; - let power = first_aura.supported_power_zones()?; - println!("Supported Keyboard Brightness:\n{:#?}", bright); - println!("Supported Aura Modes:\n{:#?}", modes); - println!("Supported Aura Zones:\n{:#?}", zones); - println!("Supported Aura Power Zones:\n{:#?}", power); - } else { - println!("No aura interface found"); - } - } else { - println!("No aura interface found"); - } - } - - Ok(()) -} - -fn handle_backlight(cmd: &BacklightCommand) -> Result<(), Box> { - if cmd.screenpad_brightness.is_none() - && cmd.screenpad_gamma.is_none() - && cmd.sync_screenpad_brightness.is_none() - { - let backlights = find_iface::("xyz.ljones.Backlight")?; - for backlight in backlights { - println!("Current screenpad settings:"); - println!(" Brightness: {}", backlight.screenpad_brightness()?); - println!(" Gamma: {}", backlight.screenpad_gamma()?); - println!( - " Sync with primary: {}", - backlight.screenpad_sync_with_primary()? - ); - } - - return Ok(()); - } - - let backlights = find_iface::("xyz.ljones.Backlight")?; - for backlight in backlights { - if let Some(brightness) = cmd.screenpad_brightness { - backlight.set_screenpad_brightness(brightness)?; - } - - if let Some(gamma) = cmd.screenpad_gamma { - backlight.set_screenpad_gamma(gamma.to_string().as_str())?; - } - - if let Some(sync) = cmd.sync_screenpad_brightness { - backlight.set_screenpad_sync_with_primary(sync)?; - } - } - - Ok(()) -} - -fn handle_brightness(cmd: &BrightnessCommand) -> Result<(), Box> { - let Ok(aura_proxies) = find_iface::("xyz.ljones.Aura") else { - println!("No aura interface found"); - return Ok(()); - }; - - match &cmd.command { - BrightnessSubCommand::Set(s) => { - for aura in aura_proxies.iter() { - if let Some(level) = s.level.level() { - aura.set_brightness(rog_aura::LedBrightness::from(level))?; - } else { - let current = aura.brightness()?; - println!("Current keyboard led brightness: {current:?}"); - } - } - } - BrightnessSubCommand::Get(_) => { - for aura in aura_proxies.iter() { - let level = aura.brightness()?; - println!("Current keyboard led brightness: {level:?}"); - } - - return Ok(()); - } - BrightnessSubCommand::Next(_) => { - for aura in aura_proxies.iter() { - let brightness = aura.brightness()?; - aura.set_brightness(brightness.next())?; - } - } - BrightnessSubCommand::Prev(_) => { - for aura in aura_proxies.iter() { - let brightness = aura.brightness()?; - aura.set_brightness(brightness.prev())?; - } - } - } - - Ok(()) -} - -fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box> { - if cmd.command.is_none() - && cmd.enable_display.is_none() - && cmd.enable_powersave_anim.is_none() - && cmd.brightness.is_none() - && cmd.off_when_lid_closed.is_none() - && cmd.off_when_suspended.is_none() - && cmd.off_when_unplugged.is_none() - && cmd.off_with_his_head.is_none() - && !cmd.clear - { - println!("Missing arg or command; run 'asusctl anime --help' for usage"); - } - - let animes = find_iface::("xyz.ljones.Anime").map_err(|e| { - error!("Did not find any interface for xyz.ljones.Anime: {e:?}"); - e - })?; - - for proxy in animes { - if let Some(enable) = cmd.enable_display { - proxy.set_enable_display(enable)?; - } - if let Some(enable) = cmd.enable_powersave_anim { - proxy.set_builtins_enabled(enable)?; - } - if let Some(bright) = cmd.brightness { - proxy.set_brightness(bright)?; - } - if let Some(enable) = cmd.off_when_lid_closed { - proxy.set_off_when_lid_closed(enable)?; - } - if let Some(enable) = cmd.off_when_suspended { - proxy.set_off_when_suspended(enable)?; - } - if let Some(enable) = cmd.off_when_unplugged { - proxy.set_off_when_unplugged(enable)?; - } - if cmd.off_with_his_head.is_some() { - println!("Did Alice _really_ make it back from Wonderland?"); - } - - let mut anime_type = get_anime_type(); - if let AnimeType::Unsupported = anime_type { - if let Some(model) = cmd.override_type { - anime_type = model; - } - } - - if cmd.clear { - let data = vec![255u8; anime_type.data_length()]; - let tmp = AnimeDataBuffer::from_vec(anime_type, data)?; - proxy.write(tmp)?; - } - - if let Some(action) = cmd.command.as_ref() { - match action { - AnimeActions::Image(image) => { - if image.path.is_empty() { - println!( - "Missing arg or command; run 'asusctl anime image --help' for usage" - ); - return Ok(()); - } - verify_brightness(image.bright); - - let matrix = AnimeImage::from_png( - Path::new(&image.path), - image.scale, - image.angle, - Vec2::new(image.x_pos, image.y_pos), - image.bright, - anime_type, - )?; - - proxy.write(::try_from(&matrix)?)?; - } - AnimeActions::PixelImage(image) => { - if image.path.is_empty() { - println!("Missing arg or command; run 'asusctl anime pixel-image --help' for usage"); - return Ok(()); - } - verify_brightness(image.bright); - - let matrix = AnimeDiagonal::from_png( - Path::new(&image.path), - None, - image.bright, - anime_type, - )?; - - proxy.write(matrix.into_data_buffer(anime_type)?)?; - } - AnimeActions::Gif(gif) => { - if gif.path.is_empty() { - println!( - "Missing arg or command; run 'asusctl anime gif --help' for usage" - ); - return Ok(()); - } - verify_brightness(gif.bright); - - let matrix = AnimeGif::from_gif( - Path::new(&gif.path), - gif.scale, - gif.angle, - Vec2::new(gif.x_pos, gif.y_pos), - AnimTime::Count(1), - gif.bright, - anime_type, - )?; - - let mut loops = gif.loops as i32; - loop { - for frame in matrix.frames() { - proxy.write(frame.frame().clone())?; - sleep(frame.delay()); - } - if loops >= 0 { - loops -= 1; - } - if loops == 0 { - break; - } - } - } - AnimeActions::PixelGif(gif) => { - if gif.path.is_empty() { - println!("Missing arg or command; run 'asusctl anime pixel-gif --help' for usage"); - return Ok(()); - } - verify_brightness(gif.bright); - - let matrix = AnimeGif::from_diagonal_gif( - Path::new(&gif.path), - AnimTime::Count(1), - gif.bright, - anime_type, - )?; - - let mut loops = gif.loops as i32; - loop { - for frame in matrix.frames() { - proxy.write(frame.frame().clone())?; - sleep(frame.delay()); - } - if loops >= 0 { - loops -= 1; - } - if loops == 0 { - break; - } - } - } - AnimeActions::SetBuiltins(builtins) => { - if builtins.set.is_none() { - println!("Missing arg; run 'asusctl anime set-builtins --help' for usage"); - return Ok(()); - } - - proxy.set_builtin_animations(rog_anime::Animations { - boot: builtins.boot, - awake: builtins.awake, - sleep: builtins.sleep, - shutdown: builtins.shutdown, - })?; - } - } + handle_info(info_opt, supported_interfaces, supported_properties)?; } } - Ok(()) -} - -fn verify_brightness(brightness: f32) { - if !(0.0..=1.0).contains(&brightness) { - println!( - "Image and global brightness must be between 0.0 and 1.0 (inclusive), was {}", - brightness - ); - } -} - -fn handle_slash(cmd: &SlashCommand) -> Result<(), Box> { - match &cmd.command { - SlashSubCommand::Get(_) => handle_slash_get()?, - SlashSubCommand::Set(cmd) => handle_slash_set(cmd)?, - SlashSubCommand::List(_) => handle_slash_list(), - } Ok(()) } - -fn handle_scsi(cmd: &ScsiCommand) -> Result<(), Box> { - if !cmd.list && cmd.enable.is_none() && cmd.mode.is_none() && cmd.colours.is_empty() { - println!("Missing arg or command; run 'asusctl scsi --help' for usage"); - } - - let scsis = find_iface::("xyz.ljones.ScsiAura")?; - - for scsi in scsis { - if let Some(enable) = cmd.enable { - scsi.set_enabled(enable)?; - } - - if let Some(mode) = cmd.mode { - scsi.set_led_mode(mode)?; - } - - let mut mode = scsi.led_mode_data()?; - let mut do_update = false; - if !cmd.colours.is_empty() { - for (count, c) in cmd.colours.iter().enumerate() { - if count == 0 { - mode.colour1 = *c; - } - if count == 1 { - mode.colour2 = *c; - } - if count == 2 { - mode.colour3 = *c; - } - if count == 3 { - mode.colour4 = *c; - } - } - do_update = true; - } - - if let Some(speed) = cmd.speed { - mode.speed = speed; - do_update = true; - } - - if let Some(dir) = cmd.direction { - mode.direction = dir; - do_update = true; - } - - if do_update { - scsi.set_led_mode_data(mode.clone())?; - } - - // let mode_ret = scsi.led_mode_data()?; - // assert_eq!(mode, mode_ret); - println!("{mode}"); - } - - if cmd.list { - let res = AuraMode::list(); - for p in &res { - println!("{:?}", p); - } - } - - Ok(()) -} - -fn handle_led_mode(mode: &LedModeCommand) -> Result<(), Box> { - if mode.command.is_none() && !mode.prev_mode && !mode.next_mode { - println!("Missing arg or command; run 'asusctl aura --help' for usage"); - // print available modes when possible - if let Ok(aura) = find_iface::("xyz.ljones.Aura") { - if let Some(first_aura) = aura.first() { - let modes = first_aura.supported_basic_modes()?; - println!("Available modes:"); - for m in modes { - println!(" {:?}", m); - } - } - } - return Ok(()); - } - - if mode.next_mode && mode.prev_mode { - println!("Please specify either next or previous"); - return Ok(()); - } - let aura = find_iface::("xyz.ljones.Aura")?; - if mode.next_mode { - for aura in aura { - let mode = aura.led_mode()?; - let modes = aura.supported_basic_modes()?; - if let Some(pos) = modes.iter().position(|m| *m == mode) { - let next_pos = if pos + 1 >= modes.len() { 0 } else { pos + 1 }; - if let Some(&target_mode) = modes.get(next_pos) { - aura.set_led_mode(target_mode)?; - } - } else if let Some(&first) = modes.first() { - aura.set_led_mode(first)?; - } - } - } else if mode.prev_mode { - for aura in aura { - let mode = aura.led_mode()?; - let modes = aura.supported_basic_modes()?; - if let Some(pos) = modes.iter().position(|m| *m == mode) { - let prev_pos = if pos == 0 { - modes.len().saturating_sub(1) - } else { - pos - 1 - }; - if let Some(&target_mode) = modes.get(prev_pos) { - aura.set_led_mode(target_mode)?; - } - } else if let Some(&last) = modes.last() { - aura.set_led_mode(last)?; - } - } - } else if let Some(mode) = mode.command.as_ref() { - for aura in aura { - aura.set_led_mode_data(::from(mode))?; - } - } - - Ok(()) -} - -fn handle_led_power1(power: &LedPowerCommand1) -> Result<(), Box> { - let aura = find_iface::("xyz.ljones.Aura")?; - for aura in aura { - let dev_type = aura.device_type()?; - if !dev_type.is_old_laptop() && !dev_type.is_tuf_laptop() { - println!("This option applies only to keyboards 2021+"); - } - - if power.awake.is_none() - && power.sleep.is_none() - && power.boot.is_none() - && !power.keyboard - && !power.lightbar - { - println!("Missing arg or command; run 'asusctl aura power-tuf --help' for usage"); - return Ok(()); - } - - if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() { - handle_led_power_1_do_1866(&aura, power)?; - return Ok(()); - } - } - - println!("These options are for keyboards of product ID 0x1866 or TUF only"); - Ok(()) -} - -fn handle_led_power_1_do_1866( - aura: &AuraProxyBlocking, - power: &LedPowerCommand1, -) -> Result<(), Box> { - let mut states = Vec::new(); - if power.keyboard { - states.push(AuraPowerState { - zone: PowerZones::Keyboard, - boot: power.boot.unwrap_or_default(), - awake: power.awake.unwrap_or_default(), - sleep: power.sleep.unwrap_or_default(), - shutdown: false, - }); - } - if power.lightbar { - states.push(AuraPowerState { - zone: PowerZones::Lightbar, - boot: power.boot.unwrap_or_default(), - awake: power.awake.unwrap_or_default(), - sleep: power.sleep.unwrap_or_default(), - shutdown: false, - }); - } - - let states = LaptopAuraPower { states }; - aura.set_led_power(states)?; - Ok(()) -} - -fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box> { - let aura = find_iface::("xyz.ljones.Aura")?; - for aura in aura { - let dev_type = aura.device_type()?; - if !dev_type.is_new_laptop() { - println!("This option applies only to keyboards 2021+"); - continue; - } - - if power.command.is_none() { - println!("Missing arg or command; run 'asusctl aura power --help' for usage"); - println!("Commands available"); - return Ok(()); - } - - if let Some(_pow) = power.command.as_ref() { - let mut states = aura.led_power()?; - let mut set = - |zone: PowerZones, boot_v: bool, awake_v: bool, sleep_v: bool, shutdown_v: bool| { - for state in states.states.iter_mut() { - if state.zone == zone { - state.boot = boot_v; - state.awake = awake_v; - state.sleep = sleep_v; - state.shutdown = shutdown_v; - break; - } - } - }; - - if let Some(cmd) = &power.command { - match cmd { - aura_cli::SetAuraZoneEnabled::Keyboard(k) => { - set(PowerZones::Keyboard, k.boot, k.awake, k.sleep, k.shutdown) - } - aura_cli::SetAuraZoneEnabled::Logo(l) => { - set(PowerZones::Logo, l.boot, l.awake, l.sleep, l.shutdown) - } - aura_cli::SetAuraZoneEnabled::Lightbar(l) => { - set(PowerZones::Lightbar, l.boot, l.awake, l.sleep, l.shutdown) - } - aura_cli::SetAuraZoneEnabled::Lid(l) => { - set(PowerZones::Lid, l.boot, l.awake, l.sleep, l.shutdown) - } - aura_cli::SetAuraZoneEnabled::RearGlow(r) => { - set(PowerZones::RearGlow, r.boot, r.awake, r.sleep, r.shutdown) - } - aura_cli::SetAuraZoneEnabled::Ally(r) => { - set(PowerZones::Ally, r.boot, r.awake, r.sleep, r.shutdown) - } - } - } - - aura.set_led_power(states)?; - } - } - - Ok(()) -} - -fn handle_throttle_profile( - conn: &Connection, - supported: &[Properties], - cmd: &ProfileCommand, -) -> Result<(), Box> { - if !supported.contains(&Properties::ThrottlePolicy) { - println!("Profiles not supported by either this kernel or by the laptop."); - return Err(ProfileError::NotSupported.into()); - } - - let proxy = PlatformProxyBlocking::new(conn)?; - let current = proxy.platform_profile()?; - let choices = proxy.platform_profile_choices()?; - - match &cmd.command { - crate::cli_opts::ProfileSubCommand::Next(_) => { - proxy.set_platform_profile(PlatformProfile::next(current, &choices))?; - } - crate::cli_opts::ProfileSubCommand::Set(s) => { - if !s.ac && !s.battery { - proxy.set_platform_profile(s.profile)?; - } else { - if s.ac { - proxy.set_platform_profile_on_ac(s.profile)?; - } - if s.battery { - proxy.set_platform_profile_on_battery(s.profile)?; - } - } - } - crate::cli_opts::ProfileSubCommand::List(_) => { - for p in &choices { - println!("{:?}", p); - } - } - crate::cli_opts::ProfileSubCommand::Get(_) => { - println!("Active profile: {current:?}"); - println!(); - println!("AC profile {:?}", proxy.platform_profile_on_ac()?); - println!("Battery profile {:?}", proxy.platform_profile_on_battery()?); - } - crate::cli_opts::ProfileSubCommand::Tuning(t) => match t.enable { - Some(true) => { - proxy.set_enable_ppt_group(true)?; - println!("Profile tuning enabled") - } - Some(false) => { - proxy.set_enable_ppt_group(false)?; - println!("Profile tuning disabled") - } - None => { - println!("Profile tuning: {}", proxy.enable_ppt_group()?) - } - }, - } - - Ok(()) -} - -fn handle_fan_curve( - conn: &Connection, - cmd: &FanCurveCommand, -) -> Result<(), Box> { - let Ok(fan_proxy) = FanCurvesProxyBlocking::new(conn).map_err(|e| { - println!("Fan-curves not supported by either this kernel or by the laptop: {e:?}"); - }) else { - return Err(ProfileError::NotSupported.into()); - }; - - if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() { - println!("Missing arg or command; run 'asusctl fan-curve --help' for usage"); - return Ok(()); - } - - if (cmd.enable_fan_curves.is_some() || cmd.fan.is_some() || cmd.data.is_some()) - && cmd.mod_profile.is_none() - { - println!( - "--enable-fan-curves, --enable-fan-curve, --fan, and --data options require \ - --mod-profile" - ); - return Ok(()); - } - - let plat_proxy = PlatformProxyBlocking::new(conn)?; - if cmd.get_enabled { - let profile = plat_proxy.platform_profile()?; - let curves = fan_proxy.fan_curve_data(profile)?; - for curve in curves.iter() { - println!("{}", String::from(curve)); - } - } - - if cmd.default { - let active = plat_proxy.platform_profile()?; - fan_proxy.set_curves_to_defaults(active)?; - } - - if let Some(profile) = cmd.mod_profile { - if cmd.enable_fan_curves.is_none() && cmd.data.is_none() { - let data = fan_proxy.fan_curve_data(profile)?; - let ron = ron::ser::to_string_pretty(&data, PrettyConfig::new().depth_limit(4))?; - println!("\nFan curves for {:?}\n\n{}", profile, ron); - } - - if let Some(enabled) = cmd.enable_fan_curves { - fan_proxy.set_fan_curves_enabled(profile, enabled)?; - } - - if let Some(enabled) = cmd.enable_fan_curve { - if let Some(fan) = cmd.fan { - fan_proxy.set_profile_fan_curve_enabled(profile, fan, enabled)?; - } else { - println!( - "--enable-fan-curves, --enable-fan-curve, --fan, and --data options require \ - --mod-profile" - ); - } - } - - if let Some(mut curve) = cmd.data.clone() { - let fan = cmd.fan.unwrap_or_default(); - curve.set_fan(fan); - fan_proxy.set_fan_curve(profile, curve)?; - } - } - - Ok(()) -} - -fn check_systemd_unit_active(name: &str) -> bool { - if let Ok(out) = Command::new("systemctl") - .arg("is-active") - .arg(name) - .output() - { - let buf = String::from_utf8_lossy(&out.stdout); - return !buf.contains("inactive") && !buf.contains("failed"); - } - false -} - -fn check_systemd_unit_enabled(name: &str) -> bool { - if let Ok(out) = Command::new("systemctl") - .arg("is-enabled") - .arg(name) - .output() - { - let buf = String::from_utf8_lossy(&out.stdout); - return buf.contains("enabled") || buf.contains("linked"); - } - false -} - -fn print_firmware_attr(attr: &AsusArmouryProxyBlocking) -> Result<(), Box> { - let name = attr.name()?; - println!("{}:", <&str>::from(name)); - - // Be resilient to DBus read failures: if any read fails, show "unavailable" - let attrs = attr.available_attrs().unwrap_or_default(); - - let has_min = attrs.contains(&"min_value".to_string()); - let has_max = attrs.contains(&"max_value".to_string()); - let has_current = attrs.contains(&"current_value".to_string()); - let has_possible = attrs.contains(&"possible_values".to_string()); - let has_default = attrs.contains(&"default_value".to_string()); - - if has_current && (has_min || has_max) { - let c = attr.current_value().ok(); - let min = if has_min { attr.min_value().ok() } else { None }; - let max = if has_max { attr.max_value().ok() } else { None }; - match (min, c, max) { - (Some(min), Some(c), Some(max)) => println!(" current: {min}..[{c}]..{max}"), - (Some(min), Some(c), None) => println!(" current: {min}..[{c}]"), - (None, Some(c), Some(max)) => println!(" current: [{c}]..{max}"), - _ => println!(" current: unavailable"), - } - - if has_default { - match attr.default_value().ok() { - Some(d) => println!(" default: {}\n", d), - None => println!(" default: unavailable\n"), - } - } else { - println!(); - } - } else if has_possible && has_current { - let c = attr.current_value().ok(); - let v = attr.possible_values().ok(); - if let (Some(c), Some(v)) = (c, v) { - for p in v.iter().enumerate() { - if p.0 == 0 { - print!(" current: ["); - } - if *p.1 == c { - print!("({c})"); - } else { - print!("{}", p.1); - } - if p.0 < v.len() - 1 { - print!(","); - } - if p.0 == v.len() - 1 { - print!("]"); - } - } - if has_default { - match attr.default_value().ok() { - Some(d) => println!(" default: {}\n", d), - None => println!(" default: unavailable\n"), - } - } else { - println!("\n"); - } - } else { - println!(" current: unavailable\n"); - } - } else if has_current { - match attr.current_value().ok() { - Some(c) => println!(" current: {c}\n"), - None => println!(" current: unavailable\n"), - } - } else { - println!(" unavailable\n"); - } - - Ok(()) -} - -#[allow(clippy::manual_is_multiple_of, clippy::nonminimal_bool)] -fn handle_armoury_command( - cmd: &ArmouryCommand, - conn: &Connection, -) -> Result<(), Box> { - // If nested subcommand provided, handle set/get/list. - match &cmd.command { - ArmourySubCommand::List(_) => { - if let Ok(attrs) = find_iface::("xyz.ljones.AsusArmoury") { - for attr in attrs.iter() { - print_firmware_attr(attr)?; - } - } - Ok(()) - } - ArmourySubCommand::Get(g) => { - let mut found = false; - let attrs = find_iface::("xyz.ljones.AsusArmoury") - .map_err(|e| format!("Could not reach asusd armoury interface: {e}"))?; - for attr in attrs.iter() { - let name = attr.name()?; - if <&str>::from(name) == g.property { - print_firmware_attr(attr)?; - found = true; - } - } - if !found { - return Err(format!("Firmware attribute '{}' not found", g.property).into()); - } - Ok(()) - } - ArmourySubCommand::Set(s) => { - let mut found = false; - let attrs = find_iface::("xyz.ljones.AsusArmoury") - .map_err(|e| format!("Could not reach asusd armoury interface: {e}"))?; - for attr in attrs.iter() { - let name = attr.name()?; - if <&str>::from(name) == s.property { - let mut value: i32 = s.value; - if value == -1 { - info!("Setting to default"); - value = attr.default_value()?; - } - attr.set_current_value(value)?; - - if attr.name()?.property_type() == FirmwareAttributeType::Ppt { - // Only Ok(true) means the value was applied to hardware now - match PlatformProxyBlocking::new(conn).and_then(|p| p.enable_ppt_group()) { - Ok(true) => {} - Ok(false) => println!( - "PPT config updated and will be applied when tuning is enabled\n\ - See: asusctl profile tuning --help" - ), - Err(e) => { - println!( - "PPT config updated, but tuning state is unknown: {e}\n\ - See: asusctl profile tuning --help" - ) - } - } - } - - print_firmware_attr(attr)?; - found = true; - } - } - if !found { - return Err(format!("Firmware attribute '{}' not found", s.property).into()); - } - Ok(()) - } - } -} diff --git a/asusctl/src/platform_cli.rs b/asusctl/src/platform_cli.rs new file mode 100644 index 00000000..50aa4454 --- /dev/null +++ b/asusctl/src/platform_cli.rs @@ -0,0 +1,582 @@ +use std::process::Command; + +use dmi_id::DMIID; +use log::{info, warn}; +use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; +use rog_aura::{AuraEffect, PowerZones}; +use rog_dbus::asus_armoury::AsusArmouryProxyBlocking; +use rog_dbus::find_iface_blocking; +use rog_dbus::zbus_aura::AuraProxyBlocking; +use rog_dbus::zbus_backlight::BacklightProxyBlocking; +use rog_dbus::zbus_platform::PlatformProxyBlocking; +use rog_platform::asus_armoury::FirmwareAttributeType; +use rog_platform::platform::{PlatformProfile, Properties}; +use rog_profiles::error::ProfileError; +use zbus::blocking::Connection; + +use crate::aura_cli::{LedPowerCommand1, LedPowerCommand2}; +use crate::cli_opts::{ + ArmouryCommand, ArmourySubCommand, BacklightCommand, BatteryCommand, BatterySubCommand, + BrightnessCommand, BrightnessSubCommand, InfoCommand, LedModeCommand, ProfileCommand, +}; + +pub fn check_service(name: &str) -> bool { + if name != "asusd" && !check_systemd_unit_enabled(name) { + warn!( + "{} is not enabled, enable it with `systemctl enable {}`", + name, name + ); + false + } else if !check_systemd_unit_active(name) { + warn!( + "{} is not running, start it with `systemctl start {}`", + name, name + ); + false + } else { + true + } +} + +pub fn check_systemd_unit_active(name: &str) -> bool { + if let Ok(out) = Command::new("systemctl") + .arg("is-active") + .arg(name) + .output() + { + let buf = String::from_utf8_lossy(&out.stdout); + return buf.trim() == "active"; + } + false +} + +pub fn check_systemd_unit_enabled(name: &str) -> bool { + if let Ok(out) = Command::new("systemctl") + .arg("is-enabled") + .arg(name) + .output() + { + let buf = String::from_utf8_lossy(&out.stdout); + return buf.contains("enabled") || buf.contains("linked"); + } + false +} + +pub fn print_info() { + let dmi = DMIID::new().unwrap_or_default(); + let board_name = dmi.board_name; + let prod_family = dmi.product_family; + info!("Software version: {}", env!("CARGO_PKG_VERSION")); + info!(" Product family: {}", prod_family.trim()); + info!(" Board name: {}", board_name.trim()); +} + +pub fn handle_info( + info_opt: &InfoCommand, + supported_interfaces: &[String], + supported_properties: &[Properties], +) -> Result<(), Box> { + info!("asusctl v{}", env!("CARGO_PKG_VERSION")); + print_info(); + + if info_opt.show_supported { + info!("Supported Core Functions:\n{:#?}", supported_interfaces); + info!( + "Supported Platform Properties:\n{:#?}", + supported_properties + ); + match find_iface_blocking::("xyz.ljones.Aura") { + Ok(aura) => { + if let Some(first_aura) = aura.first() { + let bright = first_aura.supported_brightness()?; + let modes = first_aura.supported_basic_modes()?; + let zones = first_aura.supported_basic_zones()?; + let power = first_aura.supported_power_zones()?; + info!("Supported Keyboard Brightness:\n{:#?}", bright); + info!("Supported Aura Modes:\n{:#?}", modes); + info!("Supported Aura Zones:\n{:#?}", zones); + info!("Supported Aura Power Zones:\n{:#?}", power); + } else { + warn!("No aura interface found"); + } + } + Err(err) => { + warn!("No aura interface found: {err}"); + } + } + } + + Ok(()) +} + +pub fn handle_battery( + cmd: &BatteryCommand, + conn: &Connection, +) -> Result<(), Box> { + let proxy = PlatformProxyBlocking::new(conn)?; + match &cmd.command { + BatterySubCommand::Limit(l) => { + proxy.set_charge_control_end_threshold(l.limit)?; + } + BatterySubCommand::OneShot(o) => { + if let Some(p) = o.percent { + proxy.set_charge_control_end_threshold(p)?; + } + proxy.one_shot_full_charge()?; + } + BatterySubCommand::Info(_) => { + let limit = proxy.charge_control_end_threshold()?; + info!("Current battery charge limit: {}%", limit); + } + } + + Ok(()) +} + +pub fn handle_backlight(cmd: &BacklightCommand) -> Result<(), Box> { + let backlights = find_iface_blocking::("xyz.ljones.Backlight")?; + + if cmd.screenpad_brightness.is_none() + && cmd.screenpad_gamma.is_none() + && cmd.sync_screenpad_brightness.is_none() + { + for backlight in backlights { + info!("Current screenpad settings:"); + info!(" Brightness: {}", backlight.screenpad_brightness()?); + info!(" Gamma: {}", backlight.screenpad_gamma()?); + info!( + " Sync with primary: {}", + backlight.screenpad_sync_with_primary()? + ); + } + + return Ok(()); + } + + for backlight in backlights { + if let Some(brightness) = cmd.screenpad_brightness { + backlight.set_screenpad_brightness(brightness)?; + } + + if let Some(gamma) = cmd.screenpad_gamma { + backlight.set_screenpad_gamma(gamma.to_string().as_str())?; + } + + if let Some(sync) = cmd.sync_screenpad_brightness { + backlight.set_screenpad_sync_with_primary(sync)?; + } + } + + Ok(()) +} + +pub fn handle_brightness(cmd: &BrightnessCommand) -> Result<(), Box> { + let Ok(aura_proxies) = find_iface_blocking::("xyz.ljones.Aura") else { + warn!("No aura interface found"); + return Ok(()); + }; + + match &cmd.command { + BrightnessSubCommand::Set(s) => { + for aura in aura_proxies.iter() { + if let Some(level) = s.level.level() { + aura.set_brightness(rog_aura::LedBrightness::from(level))?; + } else { + let current = aura.brightness()?; + info!("Current keyboard led brightness: {current:?}"); + } + } + } + BrightnessSubCommand::Get(_) => { + for aura in aura_proxies.iter() { + let level = aura.brightness()?; + info!("Current keyboard led brightness: {level:?}"); + } + + return Ok(()); + } + BrightnessSubCommand::Next(_) => { + for aura in aura_proxies.iter() { + let brightness = aura.brightness()?; + aura.set_brightness(brightness.next())?; + } + } + BrightnessSubCommand::Prev(_) => { + for aura in aura_proxies.iter() { + let brightness = aura.brightness()?; + aura.set_brightness(brightness.prev())?; + } + } + } + + Ok(()) +} + +pub fn handle_led_mode(mode: &LedModeCommand) -> Result<(), Box> { + if mode.command.is_none() && !mode.prev_mode && !mode.next_mode { + warn!("Missing arg or command; run 'asusctl aura --help' for usage"); + if let Ok(aura) = find_iface_blocking::("xyz.ljones.Aura") { + if let Some(first_aura) = aura.first() { + let modes = first_aura.supported_basic_modes()?; + info!("Available modes:"); + for m in modes { + info!(" {:?}", m); + } + } + } + return Ok(()); + } + + if mode.next_mode && mode.prev_mode { + warn!("Please specify either next or previous"); + return Ok(()); + } + let aura = find_iface_blocking::("xyz.ljones.Aura")?; + if mode.next_mode { + for aura in aura { + let mode = aura.led_mode()?; + let modes = aura.supported_basic_modes()?; + if let Some(pos) = modes.iter().position(|m| *m == mode) { + let next_pos = if pos + 1 >= modes.len() { 0 } else { pos + 1 }; + if let Some(&target_mode) = modes.get(next_pos) { + aura.set_led_mode(target_mode)?; + } + } else if let Some(&first) = modes.first() { + aura.set_led_mode(first)?; + } + } + } else if mode.prev_mode { + for aura in aura { + let mode = aura.led_mode()?; + let modes = aura.supported_basic_modes()?; + if let Some(pos) = modes.iter().position(|m| *m == mode) { + let prev_pos = if pos == 0 { + modes.len().saturating_sub(1) + } else { + pos - 1 + }; + if let Some(&target_mode) = modes.get(prev_pos) { + aura.set_led_mode(target_mode)?; + } + } else if let Some(&last) = modes.last() { + aura.set_led_mode(last)?; + } + } + } else if let Some(mode) = mode.command.as_ref() { + for aura in aura { + aura.set_led_mode_data(::from(mode))?; + } + } + + Ok(()) +} + +pub fn handle_led_power1(power: &LedPowerCommand1) -> Result<(), Box> { + if power.awake.is_none() + && power.sleep.is_none() + && power.boot.is_none() + && !power.keyboard + && !power.lightbar + { + warn!("Missing arg or command; run 'asusctl aura power-tuf --help' for usage"); + return Ok(()); + } + + let aura = find_iface_blocking::("xyz.ljones.Aura")?; + let mut _handled = false; + for aura in aura { + let dev_type = aura.device_type()?; + if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() { + handle_led_power_1_do_1866(&aura, power)?; + _handled = true; + } else { + warn!("These options are for keyboards of product ID 0x1866 or TUF only"); + } + } + + Ok(()) +} + +fn handle_led_power_1_do_1866( + aura: &AuraProxyBlocking, + power: &LedPowerCommand1, +) -> Result<(), Box> { + if !power.keyboard && !power.lightbar { + warn!("Must specify at least one zone: --keyboard or --lightbar"); + return Ok(()); + } + + let mut states = Vec::new(); + if power.keyboard { + states.push(AuraPowerState { + zone: PowerZones::Keyboard, + boot: power.boot.unwrap_or_default(), + awake: power.awake.unwrap_or_default(), + sleep: power.sleep.unwrap_or_default(), + shutdown: false, + }); + } + if power.lightbar { + states.push(AuraPowerState { + zone: PowerZones::Lightbar, + boot: power.boot.unwrap_or_default(), + awake: power.awake.unwrap_or_default(), + sleep: power.sleep.unwrap_or_default(), + shutdown: false, + }); + } + + let states = LaptopAuraPower { states }; + aura.set_led_power(states)?; + Ok(()) +} + +pub fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box> { + let Some(cmd) = &power.command else { + warn!("Missing arg or command; run 'asusctl aura power --help' for usage"); + return Ok(()); + }; + + let aura = find_iface_blocking::("xyz.ljones.Aura")?; + for aura in aura { + let dev_type = aura.device_type()?; + if !dev_type.is_new_laptop() { + warn!("This option applies only to keyboards 2021+"); + continue; + } + + let mut states = aura.led_power()?; + let mut set = + |zone: PowerZones, boot_v: bool, awake_v: bool, sleep_v: bool, shutdown_v: bool| { + if let Some(state) = states.states.iter_mut().find(|s| s.zone == zone) { + state.boot = boot_v; + state.awake = awake_v; + state.sleep = sleep_v; + state.shutdown = shutdown_v; + } else { + warn!("Zone {zone:?} is not supported by this device"); + } + }; + + match cmd { + crate::aura_cli::SetAuraZoneEnabled::Keyboard(k) => { + set(PowerZones::Keyboard, k.boot, k.awake, k.sleep, k.shutdown) + } + crate::aura_cli::SetAuraZoneEnabled::Logo(l) => { + set(PowerZones::Logo, l.boot, l.awake, l.sleep, l.shutdown) + } + crate::aura_cli::SetAuraZoneEnabled::Lightbar(l) => { + set(PowerZones::Lightbar, l.boot, l.awake, l.sleep, l.shutdown) + } + crate::aura_cli::SetAuraZoneEnabled::Lid(l) => { + set(PowerZones::Lid, l.boot, l.awake, l.sleep, l.shutdown) + } + crate::aura_cli::SetAuraZoneEnabled::RearGlow(r) => { + set(PowerZones::RearGlow, r.boot, r.awake, r.sleep, r.shutdown) + } + crate::aura_cli::SetAuraZoneEnabled::Ally(r) => { + set(PowerZones::Ally, r.boot, r.awake, r.sleep, r.shutdown) + } + } + + aura.set_led_power(states)?; + } + + Ok(()) +} + +pub fn handle_throttle_profile( + conn: &Connection, + supported: &[Properties], + cmd: &ProfileCommand, +) -> Result<(), Box> { + if !supported.contains(&Properties::ThrottlePolicy) { + warn!("Profiles not supported by either this kernel or by the laptop."); + return Err(ProfileError::NotSupported.into()); + } + + let proxy = PlatformProxyBlocking::new(conn)?; + let current = proxy.platform_profile()?; + let choices = proxy.platform_profile_choices()?; + + match &cmd.command { + crate::cli_opts::ProfileSubCommand::Next(_) => { + proxy.set_platform_profile(PlatformProfile::next(current, &choices))?; + } + crate::cli_opts::ProfileSubCommand::Set(s) => { + if !s.ac && !s.battery { + proxy.set_platform_profile(s.profile)?; + } else { + if s.ac { + proxy.set_platform_profile_on_ac(s.profile)?; + } + if s.battery { + proxy.set_platform_profile_on_battery(s.profile)?; + } + } + } + crate::cli_opts::ProfileSubCommand::List(_) => { + for p in &choices { + info!("{:?}", p); + } + } + crate::cli_opts::ProfileSubCommand::Get(_) => { + info!("Active profile: {current:?}"); + info!("AC profile {:?}", proxy.platform_profile_on_ac()?); + info!("Battery profile {:?}", proxy.platform_profile_on_battery()?); + } + crate::cli_opts::ProfileSubCommand::Tuning(t) => match t.enable { + Some(true) => { + proxy.set_enable_ppt_group(true)?; + info!("Profile tuning enabled"); + } + Some(false) => { + proxy.set_enable_ppt_group(false)?; + info!("Profile tuning disabled"); + } + None => { + info!("Profile tuning: {}", proxy.enable_ppt_group()?); + } + }, + } + + Ok(()) +} + +pub fn print_firmware_attr( + attr: &AsusArmouryProxyBlocking, +) -> Result<(), Box> { + let name = attr.name()?; + info!("{}:", <&str>::from(name)); + + let attrs = attr.available_attrs().unwrap_or_default(); + + let has_min = attrs.contains(&"min_value".to_string()); + let has_max = attrs.contains(&"max_value".to_string()); + let has_current = attrs.contains(&"current_value".to_string()); + let has_possible = attrs.contains(&"possible_values".to_string()); + let has_default = attrs.contains(&"default_value".to_string()); + + if has_current && (has_min || has_max) { + let c = attr.current_value().ok(); + let min = if has_min { attr.min_value().ok() } else { None }; + let max = if has_max { attr.max_value().ok() } else { None }; + match (min, c, max) { + (Some(min), Some(c), Some(max)) => info!(" current: {min}..[{c}]..{max}"), + (Some(min), Some(c), None) => info!(" current: {min}..[{c}]"), + (None, Some(c), Some(max)) => info!(" current: [{c}]..{max}"), + (None, Some(c), None) => info!(" current: {c}"), + _ => info!(" current: unavailable"), + } + + if has_default { + match attr.default_value().ok() { + Some(d) => info!(" default: {d}"), + None => info!(" default: unavailable"), + } + } + } else if has_possible && has_current { + let c = attr.current_value().ok(); + let v = attr.possible_values().ok(); + if let (Some(c), Some(v)) = (c, v) { + let mut s = String::from(" current: ["); + for (idx, item) in v.iter().enumerate() { + if *item == c { + s.push_str(&format!("({c})")); + } else { + s.push_str(&item.to_string()); + } + if idx < v.len() - 1 { + s.push(','); + } + } + s.push(']'); + info!("{s}"); + if has_default { + match attr.default_value().ok() { + Some(d) => info!(" default: {d}"), + None => info!(" default: unavailable"), + } + } + } else { + info!(" current: unavailable"); + } + } else if has_current { + match attr.current_value().ok() { + Some(c) => info!(" current: {c}"), + None => info!(" current: unavailable"), + } + } else { + info!(" unavailable"); + } + + Ok(()) +} + +pub fn handle_armoury_command( + cmd: &ArmouryCommand, + conn: &Connection, +) -> Result<(), Box> { + let attrs = find_iface_blocking::("xyz.ljones.AsusArmoury") + .map_err(|e| format!("Could not reach asusd armoury interface: {e}"))?; + match &cmd.command { + ArmourySubCommand::List(_) => { + for attr in attrs.iter() { + print_firmware_attr(attr)?; + } + Ok(()) + } + ArmourySubCommand::Get(g) => { + let mut found = false; + for attr in attrs.iter() { + let name = attr.name()?; + if <&str>::from(name) == g.property { + print_firmware_attr(attr)?; + found = true; + } + } + if !found { + return Err(format!("Firmware attribute '{}' not found", g.property).into()); + } + Ok(()) + } + ArmourySubCommand::Set(s) => { + let mut found = false; + for attr in attrs.iter() { + let name = attr.name()?; + if <&str>::from(name) == s.property { + let mut value: i32 = s.value; + if value == -1 { + info!("Setting to default"); + value = attr.default_value()?; + } + attr.set_current_value(value)?; + + if name.property_type() == FirmwareAttributeType::Ppt { + // Only Ok(true) means the value was applied to hardware now + match PlatformProxyBlocking::new(conn).and_then(|p| p.enable_ppt_group()) { + Ok(true) => {} + Ok(false) => info!( + "PPT config updated and will be applied when tuning is enabled\n\ + See: asusctl profile tuning --help" + ), + Err(e) => { + info!( + "PPT config updated, but tuning state is unknown: {e}\n\ + See: asusctl profile tuning --help" + ) + } + } + } + + print_firmware_attr(attr)?; + found = true; + } + } + if !found { + return Err(format!("Firmware attribute '{}' not found", s.property).into()); + } + Ok(()) + } + } +} diff --git a/asusctl/src/scsi_cli.rs b/asusctl/src/scsi_cli.rs index c4ae3661..e843129a 100644 --- a/asusctl/src/scsi_cli.rs +++ b/asusctl/src/scsi_cli.rs @@ -1,4 +1,6 @@ use argh::FromArgs; +use log::{info, warn}; +use rog_dbus::find_iface_blocking; use rog_scsi::{AuraMode, Colour, Direction, Speed}; #[derive(FromArgs, Debug)] @@ -28,3 +30,80 @@ pub struct ScsiCommand { #[argh(switch, description = "list available animations")] pub list: bool, } + +pub fn handle_scsi(cmd: &ScsiCommand) -> Result<(), Box> { + if cmd.list { + let res = AuraMode::list(); + for p in &res { + info!("{:?}", p); + } + return Ok(()); + } + + if cmd.enable.is_none() + && cmd.mode.is_none() + && cmd.speed.is_none() + && cmd.direction.is_none() + && cmd.colours.is_empty() + { + warn!("Missing arg or command; run 'asusctl scsi --help' for usage"); + return Ok(()); + } + + let scsis = + find_iface_blocking::("xyz.ljones.ScsiAura")?; + + if cmd.colours.len() > 4 { + warn!("Only the first 4 colours are used; ignoring the rest"); + } + + for scsi in scsis { + let res: Result<(), Box> = (|| { + if let Some(enable) = cmd.enable { + scsi.set_enabled(enable)?; + } + + if let Some(mode) = cmd.mode { + scsi.set_led_mode(mode)?; + } + + let mut mode = scsi.led_mode_data()?; + let mut do_update = false; + if !cmd.colours.is_empty() { + for (count, c) in cmd.colours.iter().enumerate() { + match count { + 0 => mode.colour1 = *c, + 1 => mode.colour2 = *c, + 2 => mode.colour3 = *c, + 3 => mode.colour4 = *c, + _ => break, + } + } + do_update = true; + } + + if let Some(speed) = cmd.speed { + mode.speed = speed; + do_update = true; + } + + if let Some(dir) = cmd.direction { + mode.direction = dir; + do_update = true; + } + + if do_update { + scsi.set_led_mode_data(mode.clone())?; + } + + info!("{mode}"); + Ok(()) + })(); + + if let Err(e) = res { + warn!("Failed to set SCSI LED mode: {e}"); + } + } + + Ok(()) +} diff --git a/asusctl/src/slash_cli.rs b/asusctl/src/slash_cli.rs index cbc6e30a..f000c514 100644 --- a/asusctl/src/slash_cli.rs +++ b/asusctl/src/slash_cli.rs @@ -1,4 +1,5 @@ use argh::FromArgs; +use log::{info, warn}; use rog_dbus::zbus_slash::SlashProxyBlocking; use rog_slash::SlashMode; use zbus::blocking::Connection; @@ -66,7 +67,10 @@ pub struct SlashSetCommand { #[argh(subcommand, name = "list", description = "list available animations")] pub struct SlashListCommand {} -pub fn handle_slash_set(cmd: &SlashSetCommand) -> Result<(), Box> { +pub fn handle_slash_set( + cmd: &SlashSetCommand, + conn: &Connection, +) -> Result<(), Box> { if cmd.brightness.is_none() && cmd.interval.is_none() && cmd.show_on_boot.is_none() @@ -78,11 +82,11 @@ pub fn handle_slash_set(cmd: &SlashSetCommand) -> Result<(), Box Result<(), Box Result<(), Box> { - let conn = Connection::system()?; - let proxy = SlashProxyBlocking::new(&conn) +pub fn handle_slash_get(conn: &Connection) -> Result<(), Box> { + let proxy = SlashProxyBlocking::new(conn) .map_err(|e| format!("Failed to connect to Slash interface: {e}"))?; let enabled = proxy.enabled()?; @@ -134,18 +137,18 @@ pub fn handle_slash_get() -> Result<(), Box> { let show_on_battery = proxy.show_on_battery()?; let show_battery_warning = proxy.show_battery_warning()?; - println!( + info!( "Slash LED: {}", if enabled { "enabled" } else { "disabled" } ); - println!("Brightness: {}", brightness); - println!("Interval: {}", interval); - println!("Mode: {}", mode); - println!("Show on boot: {}", show_on_boot); - println!("Show on shutdown: {}", show_on_shutdown); - println!("Show on sleep: {}", show_on_sleep); - println!("Show on battery: {}", show_on_battery); - println!("Show battery warning: {}", show_battery_warning); + info!("Brightness: {brightness}"); + info!("Interval: {interval}"); + info!("Mode: {mode}"); + info!("Show on boot: {show_on_boot}"); + info!("Show on shutdown: {show_on_shutdown}"); + info!("Show on sleep: {show_on_sleep}"); + info!("Show on battery: {show_on_battery}"); + info!("Show battery warning: {show_battery_warning}"); Ok(()) } @@ -153,6 +156,6 @@ pub fn handle_slash_get() -> Result<(), Box> { pub fn handle_slash_list() { let res = SlashMode::list(); for p in &res { - println!("{p}"); + info!("{p}"); } } diff --git a/asusctl/src/xgm_led_cli.rs b/asusctl/src/xgm_led_cli.rs index 4e6bfe86..cf74bcc2 100644 --- a/asusctl/src/xgm_led_cli.rs +++ b/asusctl/src/xgm_led_cli.rs @@ -1,19 +1,22 @@ use crate::cli_opts::XgmLedSubCommand; use rog_dbus::zbus_xgm_led::XgmLedProxyBlocking; -pub fn handle_xgm_led(cmd: &XgmLedSubCommand) -> Result<(), Box> { - let proxy = XgmLedProxyBlocking::new(&zbus::blocking::Connection::system()?) +pub fn handle_xgm_led( + conn: &zbus::blocking::Connection, + cmd: &XgmLedSubCommand, +) -> Result<(), Box> { + let proxy = XgmLedProxyBlocking::new(conn) .map_err(|e| format!("Failed to connect to XG Mobile LED interface: {e}"))?; match cmd { XgmLedSubCommand::Get(_) => { let enabled = proxy.xgm_led_enabled()?; - println!("XG Mobile LED: {}", if enabled { "ON" } else { "OFF" }); + log::info!("XG Mobile LED: {}", if enabled { "ON" } else { "OFF" }); } XgmLedSubCommand::Set(cmd) => { let enabled = cmd.value != 0; proxy.set_xgm_led_enabled(enabled)?; - println!( + log::info!( "XG Mobile LED set to {}", if enabled { "ON" } else { "OFF" } ); diff --git a/asusd/src/daemon.rs b/asusd/src/daemon.rs index 02f4025a..44e32907 100644 --- a/asusd/src/daemon.rs +++ b/asusd/src/daemon.rs @@ -21,9 +21,6 @@ use zbus::fdo::ObjectManager; #[tokio::main] async fn main() -> Result<(), Box> { - println!("Starting asusd daemon..."); - - // console_subscriber::init(); let mut logger = env_logger::Builder::new(); logger .parse_default_env() @@ -32,17 +29,17 @@ async fn main() -> Result<(), Box> { .filter_level(log::LevelFilter::Debug) .init(); + info!("Starting asusd daemon..."); + let is_service = match env::var_os("IS_SERVICE") { Some(val) => val == "1", None => true, }; if !is_service { - println!("asusd schould be only run from the right systemd service"); - println!( - "do not run in your terminal, if you need an logs please use journalctl -b -u asusd" - ); - println!("asusd will now exit"); + warn!("asusd should only be run from the proper systemd service"); + warn!("do not run in your terminal; if you need logs, please use journalctl -b -u asusd"); + warn!("asusd will now exit"); return Ok(()); } @@ -59,9 +56,7 @@ async fn main() -> Result<(), Box> { /// The actual main loop for the daemon async fn start_daemon() -> Result<(), Box> { - // let supported = SupportedFunctions::get_supported(); print_board_info(); - // println!("{:?}", supported.supported_functions()); // Start zbus server let mut server = Connection::system().await?; diff --git a/rog-anime/src/data.rs b/rog-anime/src/data.rs index bd0451b2..e1872cb9 100644 --- a/rog-anime/src/data.rs +++ b/rog-anime/src/data.rs @@ -4,7 +4,7 @@ use std::thread::sleep; use std::time::{Duration, Instant}; use dmi_id::DMIID; -use log::info; +use log::{info, warn}; use serde::{Deserialize, Serialize}; #[cfg(feature = "dbus")] use zbus::zvariant::{OwnedValue, Type, Value}; @@ -317,7 +317,7 @@ pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Re fade_out_step = 1.0 / fade_out.as_secs_f32(); if time.total_fade_time() > run_time { - println!("Total fade in/out time larger than gif run time. Setting fades to half"); + warn!("Total fade in/out time larger than gif run time. Setting fades to half"); fade_in = run_time / 2; fade_in_step = 1.0 / (run_time / 2).as_secs_f32(); diff --git a/rog-aura/src/keyboard/layouts.rs b/rog-aura/src/keyboard/layouts.rs index 4a363128..20e7db18 100644 --- a/rog-aura/src/keyboard/layouts.rs +++ b/rog-aura/src/keyboard/layouts.rs @@ -305,7 +305,7 @@ impl KeyLayout { let mut files = Vec::new(); std::fs::read_dir(path) .map_err(|e| { - println!("{:?}, {e}", path); + log::warn!("{:?}, {e}", path); e }) .unwrap() @@ -471,7 +471,7 @@ mod tests { let path = data_path.as_path(); for p in fs::read_dir(path) .map_err(|e| { - println!("{:?}, {e}", path); + log::warn!("{:?}, {e}", path); e }) .unwrap() diff --git a/rog-control-center/src/lib.rs b/rog-control-center/src/lib.rs index 7c07caf8..47b6970b 100644 --- a/rog-control-center/src/lib.rs +++ b/rog-control-center/src/lib.rs @@ -22,15 +22,15 @@ pub const APP_ID: &str = "org.opengamingcollective.rog-control-center"; pub const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png"; pub fn print_versions() { - println!("App and daemon versions:"); - println!(" rog-gui v{}", VERSION); - println!(" asusd v{}", asusd::VERSION); - println!("\nComponent crate versions:"); - println!(" rog-anime v{}", rog_anime::VERSION); - println!(" rog-aura v{}", rog_aura::VERSION); - println!(" rog-dbus v{}", rog_dbus::VERSION); - println!(" rog-profiles v{}", rog_profiles::VERSION); - println!("rog-platform v{}", rog_platform::VERSION); + log::info!("App and daemon versions:"); + log::info!(" rog-gui v{}", VERSION); + log::info!(" asusd v{}", asusd::VERSION); + log::info!("\nComponent crate versions:"); + log::info!(" rog-anime v{}", rog_anime::VERSION); + log::info!(" rog-aura v{}", rog_aura::VERSION); + log::info!(" rog-dbus v{}", rog_dbus::VERSION); + log::info!(" rog-profiles v{}", rog_profiles::VERSION); + log::info!(" rog-platform v{}", rog_platform::VERSION); } #[derive(PartialEq, Eq, Clone, Copy)] diff --git a/rog-control-center/src/main.rs b/rog-control-center/src/main.rs index 814b6bc1..81361db0 100644 --- a/rog-control-center/src/main.rs +++ b/rog-control-center/src/main.rs @@ -27,7 +27,7 @@ use tokio::runtime::Runtime; async fn main() -> Result<()> { // Ensure tracing spans are quiet by default unless user overrides if std::env::var_os("RUST_LOG").is_none() { - std::env::set_var("RUST_LOG", "warn,tracing=error,zbus=error"); + std::env::set_var("RUST_LOG", "info,tracing=error,zbus=error"); } let mut logger = env_logger::Builder::new(); logger @@ -83,12 +83,12 @@ async fn main() -> Result<()> { let asusd_version = match platform_proxy.version() { Ok(v) => v, Err(e) => { - eprintln!("Could not get asusd version: {e:?}\nIs asusd.service running?"); + error!("Could not get asusd version: {e:?}\nIs asusd.service running?"); std::process::exit(1); } }; if asusd_version != self_version { - println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); + warn!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); // return Ok(()); } diff --git a/rog-control-center/src/zbus_proxies.rs b/rog-control-center/src/zbus_proxies.rs index d1e01614..a6ef25f6 100644 --- a/rog-control-center/src/zbus_proxies.rs +++ b/rog-control-center/src/zbus_proxies.rs @@ -1,8 +1,5 @@ -use log::info; use std::sync::{Arc, Mutex}; -use zbus::blocking::proxy::ProxyImpl; -use zbus::blocking::{fdo, Connection}; use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::{interface, proxy}; @@ -71,79 +68,4 @@ pub trait ROGCCZbus { fn set_state(&self, state: AppState) -> zbus::Result<()>; } -pub fn find_iface(iface_name: &str) -> Result, Box> -where - T: ProxyImpl<'static> + From>, -{ - let conn = Connection::system()?; - let f = fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/")?; - let interfaces = f.get_managed_objects()?; - let mut paths = Vec::new(); - for v in interfaces.iter() { - // let o: Vec = v.1.keys().map(|e| - // e.to_owned()).collect(); println!("{}, {:?}", v.0, o); - for k in v.1.keys() { - if k.as_str() == iface_name { - // println!("Found {iface_name} device at {}, {}", v.0, k); - paths.push(v.0.clone()); - } - } - } - if paths.len() > 1 { - info!("Multiple asusd interfaces devices found"); - } - if !paths.is_empty() { - let mut ctrl = Vec::new(); - paths.sort_by(|a, b| a.cmp(b)); - for path in paths { - ctrl.push( - T::builder(&conn) - .path(path.clone())? - .destination("xyz.ljones.Asusd")? - .build()?, - ); - } - return Ok(ctrl); - } - - Err("No Aura interface".into()) -} - -pub async fn find_iface_async(iface_name: &str) -> Result, Box> -where - T: zbus::proxy::ProxyImpl<'static> + From>, -{ - let conn = zbus::Connection::system().await?; - let f = zbus::fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/").await?; - let interfaces = f.get_managed_objects().await?; - let mut paths = Vec::new(); - for v in interfaces.iter() { - // let o: Vec = v.1.keys().map(|e| - // e.to_owned()).collect(); println!("{}, {:?}", v.0, o); - for k in v.1.keys() { - if k.as_str() == iface_name { - // println!("Found {iface_name} device at {}, {}", v.0, k); - paths.push(v.0.clone()); - } - } - } - if paths.len() > 1 { - info!("Multiple asusd interfaces devices found"); - } - if !paths.is_empty() { - let mut ctrl = Vec::new(); - paths.sort_by(|a, b| a.cmp(b)); - for path in paths { - ctrl.push( - T::builder(&conn) - .path(path.clone())? - .destination("xyz.ljones.Asusd")? - .build() - .await?, - ); - } - return Ok(ctrl); - } - - Err("No interface".into()) -} +pub use rog_dbus::{find_iface_async, find_iface_blocking}; diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index 29c7759d..6540794e 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -17,5 +17,6 @@ rog_scsi = { path = "../rog-scsi", features = ["dbus"] } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } rog_platform = { path = "../rog-platform" } +log.workspace = true zbus.workspace = true diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index 19cfc817..337fd8ff 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -73,7 +73,7 @@ where } } if paths.len() > 1 { - println!("Multiple asusd interfaces devices found"); + log::warn!("Multiple asusd interface devices found"); } if !paths.is_empty() { let mut ctrl = Vec::new(); @@ -92,3 +92,38 @@ where Err(format!("Did not find {iface_name}").into()) } + +pub fn find_iface_blocking(iface_name: &str) -> Result, Box> +where + T: zbus::blocking::proxy::ProxyImpl<'static> + From>, +{ + let conn = zbus::blocking::Connection::system()?; + let f = zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/")?; + let interfaces = f.get_managed_objects()?; + let mut paths = Vec::new(); + for v in interfaces.iter() { + for k in v.1.keys() { + if k.as_str() == iface_name { + paths.push(v.0.clone()); + } + } + } + if paths.len() > 1 { + log::warn!("Multiple asusd interface devices found"); + } + if !paths.is_empty() { + let mut ctrl = Vec::new(); + paths.sort_by(|a, b| a.cmp(b)); + for path in paths { + ctrl.push( + T::builder(&conn) + .path(path)? + .destination("xyz.ljones.Asusd")? + .build()?, + ); + } + return Ok(ctrl); + } + + Err(format!("Did not find {iface_name}").into()) +} diff --git a/simulators/src/simulator.rs b/simulators/src/simulator.rs index 4305d1ac..85c311f7 100644 --- a/simulators/src/simulator.rs +++ b/simulators/src/simulator.rs @@ -118,7 +118,7 @@ impl VirtAnimeMatrix { fn main() -> Result<(), Box> { let args: Vec = env::args().collect(); if args.len() <= 1 { - println!("Must supply arg, one of "); + log::warn!("Must supply arg, one of "); return Ok(()); } let anime_type = AnimeType::from_str(&args[1])?;