Compare commits

..

2 Commits

Author SHA1 Message Date
Denis Benato
e512d0e2a0 fix: do not fail when attributes are unavailable 2026-02-01 16:55:38 +01:00
Denis Benato
8161005096 fix: refresh ppt sliders when power profile gets changed 2026-02-01 16:50:45 +01:00
9 changed files with 102 additions and 180 deletions

View File

@@ -1,5 +1,10 @@
# Changelog
## [Unreleased]
### Changes
- Fix PPT sliders not updating
## [6.3.2]
### Changes

View File

@@ -970,52 +970,72 @@ fn print_firmware_attr(attr: &AsusArmouryProxyBlocking) -> Result<(), Box<dyn st
let name = attr.name()?;
println!("{}:", <&str>::from(name));
let attrs = attr.available_attrs()?;
if attrs.contains(&"min_value".to_string())
&& attrs.contains(&"max_value".to_string())
&& attrs.contains(&"current_value".to_string())
{
let c = attr.current_value()?;
let min = attr.min_value()?;
let max = attr.max_value()?;
println!(" current: {min}..[{c}]..{max}");
if attrs.contains(&"default_value".to_string()) {
println!(" default: {}\n", attr.default_value()?);
// 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_min && has_max && has_current {
let c = attr.current_value().ok();
let min = attr.min_value().ok();
let max = attr.max_value().ok();
match (min, c, max) {
(Some(min), Some(c), Some(max)) => println!(" current: {min}..[{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 attrs.contains(&"possible_values".to_string())
&& attrs.contains(&"current_value".to_string())
{
let c = attr.current_value()?;
let v = attr.possible_values()?;
for p in v.iter().enumerate() {
if p.0 == 0 {
print!(" current: [");
} 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 *p.1 == c {
print!("({c})");
if has_default {
match attr.default_value().ok() {
Some(d) => println!(" default: {}\n", d),
None => println!(" default: unavailable\n"),
}
} else {
print!("{}", p.1);
println!("\n");
}
if p.0 < v.len() - 1 {
print!(",");
}
if p.0 == v.len() - 1 {
print!("]");
}
}
if attrs.contains(&"default_value".to_string()) {
println!(" default: {}\n", attr.default_value()?);
} else {
println!("\n");
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 if attrs.contains(&"current_value".to_string()) {
let c = attr.current_value()?;
println!(" current: {c}\n");
} else {
println!();
println!(" unavailable\n");
}
Ok(())
}

View File

@@ -25,30 +25,6 @@ pub struct Aura {
impl Aura {
/// Initialise the device if required.
pub async fn do_initialization(&self) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
let hid = hid.lock().await;
let init_1: [u8; 2] = [
0x5d, 0xb9,
];
let init_2 = b"]ASUS Tech.Inc.";
let init_3: [u8; 6] = [
0x5d, 0x05, 0x20, 0x31, 0, 0x1a,
];
hid.write_bytes(&init_1)?;
hid.write_bytes(init_2)?;
hid.write_bytes(&init_3)?;
let config = self.config.lock().await;
if config.support_data.device_name.contains("GZ30")
|| config.support_data.device_name.contains("Z13")
{
let z13_init: [u8; 4] = [
0x5d, 0xc0, 0x03, 0x01,
];
hid.write_bytes(&z13_init)?;
}
}
Ok(())
}
@@ -176,54 +152,9 @@ impl Aura {
}
}
let mut enabled = config.enabled.clone();
if config.support_data.device_name.contains("GZ30")
|| config.support_data.device_name.contains("Z13")
{
let logo_state = enabled
.states
.iter()
.find(|s| s.zone == PowerZones::Logo)
.cloned();
if let Some(logo) = logo_state {
let mut lid_found = false;
let mut bar_found = false;
for s in enabled.states.iter_mut() {
if s.zone == PowerZones::Lid {
s.boot = logo.boot;
s.awake = logo.awake;
s.sleep = logo.sleep;
s.shutdown = logo.shutdown;
lid_found = true;
}
if s.zone == PowerZones::Lightbar {
s.boot = logo.boot;
s.awake = logo.awake;
s.sleep = logo.sleep;
s.shutdown = logo.shutdown;
bar_found = true;
}
}
if !lid_found {
let mut new_state = logo;
new_state.zone = PowerZones::Lid;
enabled.states.push(new_state.clone());
new_state.zone = PowerZones::Lightbar;
enabled.states.push(new_state);
} else if !bar_found {
// Lid found but not bar?
let mut new_state = logo;
new_state.zone = PowerZones::Lightbar;
enabled.states.push(new_state);
}
}
}
let bytes = enabled.to_bytes(config.led_type);
let bytes = config.enabled.to_bytes(config.led_type);
let msg = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3], 0xff,
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3],
];
hid_raw.write_bytes(&msg)?;
}

View File

@@ -654,6 +654,15 @@ impl CtrlPlatform {
.enabled = enable;
self.config.lock().await.write();
// Re-emit armoury attribute limits so GUI sees updated min/max for PPT
// attributes which can change when enabling/disabling PPT tuning groups.
// Fire-and-forget: we don't want to fail the property call if emit fails.
let _ = self
.armoury_registry
.emit_limits(&self.connection)
.await
.map_err(|e| log::error!("Failed to emit armoury limits: {e:?}"));
Ok(())
}
}
@@ -712,6 +721,13 @@ impl ReloadAndNotify for CtrlPlatform {
*config = data;
config.base_charge_control_end_threshold =
base_charge_control_end_threshold.unwrap_or_default();
// Ensure any armoury limits changes from the new config are emitted
let _ = self
.armoury_registry
.emit_limits(&self.connection)
.await
.map_err(|e| log::error!("Failed to emit armoury limits after reload: {e:?}"));
}
Ok(())
}

View File

@@ -980,24 +980,6 @@
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "GZ302",
product_id: "18c6",
layout_name: "",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Logo],
),
(
device_name: "GZ302",
product_id: "1a30",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "RC71L",
product_id: "",

View File

@@ -123,8 +123,7 @@ impl AuraPowerState {
| ((self.shutdown as u32) << 7)
}
PowerZones::Lightbar => {
((self.awake as u32) << (7 + 1))
| ((self.boot as u32) << (7 + 2))
((self.boot as u32) << (7 + 2))
| ((self.awake as u32) << (7 + 3))
| ((self.sleep as u32) << (7 + 4))
| ((self.shutdown as u32) << (7 + 5))
@@ -134,20 +133,12 @@ impl AuraPowerState {
| ((self.awake as u32) << (15 + 2))
| ((self.sleep as u32) << (15 + 3))
| ((self.shutdown as u32) << (15 + 4))
| ((self.boot as u32) << (15 + 5))
| ((self.awake as u32) << (15 + 6))
| ((self.sleep as u32) << (15 + 7))
| ((self.shutdown as u32) << (15 + 8))
}
PowerZones::RearGlow => {
((self.boot as u32) << (23 + 1))
| ((self.awake as u32) << (23 + 2))
| ((self.sleep as u32) << (23 + 3))
| ((self.shutdown as u32) << (23 + 4))
| ((self.boot as u32) << (23 + 5))
| ((self.awake as u32) << (23 + 6))
| ((self.sleep as u32) << (23 + 7))
| ((self.shutdown as u32) << (23 + 8))
}
PowerZones::None | PowerZones::KeyboardAndLightbar => 0,
}
@@ -627,19 +618,19 @@ mod test {
assert_eq!(shut_keyb_, "10000000, 00000000, 00000000, 00000000");
//
assert_eq!(boot_bar__, "00000000, 00000010, 00000000, 00000000");
assert_eq!(awake_bar_, "00000000, 00000101, 00000000, 00000000");
assert_eq!(awake_bar_, "00000000, 00000100, 00000000, 00000000");
assert_eq!(sleep_bar_, "00000000, 00001000, 00000000, 00000000");
assert_eq!(shut_bar__, "00000000, 00010000, 00000000, 00000000");
//
assert_eq!(boot_lid__, "00000000, 00000000, 00010001, 00000000");
assert_eq!(awake_lid_, "00000000, 00000000, 00100010, 00000000");
assert_eq!(sleep_lid_, "00000000, 00000000, 01000100, 00000000");
assert_eq!(shut_lid__, "00000000, 00000000, 10001000, 00000000");
assert_eq!(boot_lid__, "00000000, 00000000, 00000001, 00000000");
assert_eq!(awake_lid_, "00000000, 00000000, 00000010, 00000000");
assert_eq!(sleep_lid_, "00000000, 00000000, 00000100, 00000000");
assert_eq!(shut_lid__, "00000000, 00000000, 00001000, 00000000");
//
assert_eq!(boot_rear_, "00000000, 00000000, 00000000, 00010001");
assert_eq!(awake_rear, "00000000, 00000000, 00000000, 00100010");
assert_eq!(sleep_rear, "00000000, 00000000, 00000000, 01000100");
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 10001000");
assert_eq!(boot_rear_, "00000000, 00000000, 00000000, 00000001");
assert_eq!(awake_rear, "00000000, 00000000, 00000000, 00000010");
assert_eq!(sleep_rear, "00000000, 00000000, 00000000, 00000100");
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on
let byte1 = to_binary_string_post2021(&LaptopAuraPower {
@@ -666,6 +657,6 @@ mod test {
},
],
});
assert_eq!(byte1, "11111111, 00011111, 11111111, 11111111");
assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111");
}
}

View File

@@ -106,10 +106,10 @@ impl From<&str> for AuraDeviceType {
match s.to_lowercase().trim_start_matches("0x") {
"tuf" => AuraDeviceType::LaptopKeyboardTuf,
"1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
"1abe" | "1b4c" => Self::Ally,
"19b3" | "193b" => Self::AnimeOrSlash,
"19b6" | "1a30" | "18c6" => Self::LaptopKeyboard2021,
"19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown,
}
}

View File

@@ -162,6 +162,7 @@ impl CurveData {
/// Write this curve to the device fan specified by `self.fan`
pub fn write_to_device(&self, device: &mut Device) -> std::io::Result<()> {
let pwm_num: char = self.fan.into();
let enable = if self.enabled { '1' } else { '2' };
for (index, out) in self.pwm.iter().enumerate() {
let pwm = pwm_str(pwm_num, index);
@@ -175,20 +176,10 @@ impl CurveData {
device.set_attribute_value(&temp, out.to_string())?;
}
// Note: pwm_enable is set by write_profile_curve_to_platform after all
// curves are written, because on some devices (e.g., ASUS Z13 2025)
// setting any pwm_enable to 2 resets ALL fan enables.
Ok(())
}
/// Set the enable state for this fan curve
pub fn set_enable(&self, device: &mut Device) -> std::io::Result<()> {
let pwm_num: char = self.fan.into();
let enable = if self.enabled { "1" } else { "2" };
let enable_attr = format!("pwm{pwm_num}_enable");
// Enable must be done *after* all points are written pwm3_enable
device
.set_attribute_value(&enable_attr, enable.to_string())
.map_err(|e| error!("Failed to set {enable_attr} to {enable}: {e:?}"))
.set_attribute_value(format!("pwm{pwm_num}_enable"), enable.to_string())
.map_err(|e| error!("Failed to set pwm{pwm_num}_enable to {enable}: {e:?}"))
.ok();
Ok(())
}

View File

@@ -181,29 +181,15 @@ impl FanCurveProfiles {
PlatformProfile::Quiet | PlatformProfile::LowPower => &mut self.quiet,
PlatformProfile::Custom => &mut self.custom,
};
// First write all curve data (pwm/temp values) for all fans
for fan in fans.iter() {
debug!("write_profile_curve_to_platform: writing curve data for profile:{profile}, {fan:?}");
for fan in fans.iter().filter(|f| !f.enabled) {
debug!("write_profile_curve_to_platform: writing profile:{profile}, {fan:?}");
fan.write_to_device(device)?;
}
// Then set enables: disabled fans first, then enabled fans last.
// This order is important because on some devices (e.g., ASUS Z13 2025)
// setting any pwm_enable to 2 (disabled) resets ALL fan enables.
for fan in fans.iter().filter(|f| !f.enabled) {
debug!(
"write_profile_curve_to_platform: disabling fan for profile:{profile}, {:?}",
fan.fan
);
fan.set_enable(device)?;
}
// Write enabled fans last because the kernel currently resets *all* if one is
// disabled
for fan in fans.iter().filter(|f| f.enabled) {
debug!(
"write_profile_curve_to_platform: enabling fan for profile:{profile}, {:?}",
fan.fan
);
fan.set_enable(device)?;
debug!("write_profile_curve_to_platform: writing profile:{profile}, {fan:?}");
fan.write_to_device(device)?;
}
Ok(())
}