From 5600c51ba0fafcb250af12de96e651ead0e0cef8 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Tue, 24 Jan 2023 10:13:09 +1300 Subject: [PATCH] Fix remove the leftover initial config writes on `new()` for some controllers Closes #320 --- CHANGELOG.md | 2 + Cargo.lock | 20 +++--- Cargo.toml | 2 +- config-traits/src/lib.rs | 26 ++++--- daemon/src/config.rs | 2 +- daemon/src/ctrl_anime/config.rs | 8 +-- daemon/src/ctrl_aura/config.rs | 3 +- daemon/src/ctrl_profiles/config.rs | 38 ++--------- daemon/src/ctrl_profiles/controller.rs | 90 +++++++++++++++++++------ daemon/src/ctrl_profiles/trait_impls.rs | 49 +++++++------- 10 files changed, 135 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0a0948..a37d9322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ROGCC: Add CLI opt for viewing all layout files + filenames to help find a layout matching your laptop + Both of these options would hopefully be temporary and replaced with a "wizard" GUI helper - Fix profile controller not detecting if platform_profile is changed +- Fix remove the leftover initial config writes on `new()` for some controllers to prevent resetting settings on startup + + refactor the loading of systemd curve defaults and config file ### BREAKING - Rename aura dbus method from `per_key_raw` to `direct_addressing_raw` and add doc comment - Changes to aura.conf: diff --git a/Cargo.lock b/Cargo.lock index 84c805bc..f9bb95ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,7 +149,7 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "asusctl" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "daemon", @@ -666,7 +666,7 @@ dependencies = [ [[package]] name = "config-traits" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "log", @@ -798,7 +798,7 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "daemon" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "async-trait", "cargo-husky", @@ -822,7 +822,7 @@ dependencies = [ [[package]] name = "daemon-user" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "config-traits", @@ -2717,7 +2717,7 @@ dependencies = [ [[package]] name = "rog-control-center" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "daemon", @@ -2749,7 +2749,7 @@ dependencies = [ [[package]] name = "rog_anime" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "gif", @@ -2766,7 +2766,7 @@ dependencies = [ [[package]] name = "rog_aura" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "log", @@ -2779,7 +2779,7 @@ dependencies = [ [[package]] name = "rog_dbus" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "rog_anime", @@ -2791,7 +2791,7 @@ dependencies = [ [[package]] name = "rog_platform" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "concat-idents", @@ -2808,7 +2808,7 @@ dependencies = [ [[package]] name = "rog_profiles" -version = "4.6.0-rc1" +version = "4.6.0-rc2" dependencies = [ "cargo-husky", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5c77e0ec..b1c25b29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = ["asusctl", "config-traits", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"] [workspace.package] -version = "4.6.0-rc1" +version = "4.6.0-rc2" [workspace.dependencies] async-trait = "^0.1" diff --git a/config-traits/src/lib.rs b/config-traits/src/lib.rs index b70f4c6f..982a4a55 100644 --- a/config-traits/src/lib.rs +++ b/config-traits/src/lib.rs @@ -163,13 +163,13 @@ macro_rules! std_config_load { /// use serde::{Deserialize, Serialize}; /// use config_traits::{StdConfig, StdConfigLoad2}; /// - /// #[derive(Deserialize, Serialize)] + /// #[derive(Deserialize, Serialize, Debug)] /// struct FanCurveConfigOld {} /// - /// #[derive(Deserialize, Serialize)] + /// #[derive(Deserialize, Serialize, Debug)] /// struct FanCurveConfigOlder {} /// - /// #[derive(Deserialize, Serialize)] + /// #[derive(Deserialize, Serialize, Debug)] /// struct FanCurveConfig {} /// /// impl From for FanCurveConfig { @@ -195,7 +195,7 @@ macro_rules! std_config_load { /// new one created pub trait $trait_name<$($generic),*> where - Self: $crate::StdConfig + DeserializeOwned + Serialize, + Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize, $($generic: DeserializeOwned + Into),* { fn load(mut self) -> Self { @@ -205,16 +205,22 @@ macro_rules! std_config_load { if read_len != 0 { if let Ok(data) = ron::from_str(&buf) { self = data; + log::info!("Parsed RON for {:?}", std::any::type_name::()); } else if let Ok(data) = serde_json::from_str(&buf) { self = data; + log::info!("Parsed JSON for {:?}", std::any::type_name::()); } else if let Ok(data) = toml::from_str(&buf) { self = data; + log::info!("Parsed TOML for {:?}", std::any::type_name::()); } $(else if let Ok(data) = ron::from_str::<$generic>(&buf) { self = data.into(); + log::info!("New version failed, trying previous: Parsed RON for {:?}", std::any::type_name::<$generic>()); } else if let Ok(data) = serde_json::from_str::<$generic>(&buf) { self = data.into(); + log::info!("New version failed, trying previous: Parsed JSON for {:?}", std::any::type_name::<$generic>()); } else if let Ok(data) = toml::from_str::<$generic>(&buf) { self = data.into(); + log::info!("Newvious version failed, trying previous: Parsed TOML for {:?}", std::any::type_name::<$generic>()); })* else { self.rename_file_old(); self = Self::new(); @@ -242,10 +248,10 @@ mod tests { #[test] fn check_macro_from_1() { - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Test {} - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Old1 {} impl crate::StdConfig for Test { @@ -273,16 +279,16 @@ mod tests { #[test] fn check_macro_from_3() { - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Test {} - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Old1 {} - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Old2 {} - #[derive(serde::Deserialize, serde::Serialize)] + #[derive(serde::Deserialize, serde::Serialize, Debug)] struct Old3 {} impl crate::StdConfig for Test { diff --git a/daemon/src/config.rs b/daemon/src/config.rs index f156cd4b..a1669651 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; const CONFIG_FILE: &str = "asusd.ron"; -#[derive(Deserialize, Serialize, Default)] +#[derive(Deserialize, Serialize, Default, Debug)] pub struct Config { /// Save charge limit for restoring on boot pub bat_charge_limit: u8, diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs index e0fb9e6a..7ad8d76e 100644 --- a/daemon/src/ctrl_anime/config.rs +++ b/daemon/src/ctrl_anime/config.rs @@ -110,7 +110,7 @@ impl AnimeConfigCached { } /// Config for base system actions for the anime display -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] pub struct AnimeConfig { pub system: Vec, pub boot: Vec, @@ -164,7 +164,7 @@ impl AnimeConfig { fn create_default() -> Self { // create a default config here - let config = AnimeConfig { + AnimeConfig { system: vec![], boot: vec![ActionLoader::ImageAnimation { file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), @@ -201,8 +201,6 @@ impl AnimeConfig { brightness: 1.0, awake_enabled: true, boot_anim_enabled: true, - }; - config.write(); - config + } } } diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs index e641dd00..a2996bda 100644 --- a/daemon/src/ctrl_aura/config.rs +++ b/daemon/src/ctrl_aura/config.rs @@ -113,7 +113,7 @@ impl From<&AuraPowerConfig> for AuraPowerDev { } } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] // #[serde(default)] pub struct AuraConfig { pub brightness: LedBrightness, @@ -232,7 +232,6 @@ impl AuraConfig { } } } - config.write(); config } diff --git a/daemon/src/ctrl_profiles/config.rs b/daemon/src/ctrl_profiles/config.rs index 19996282..a66f8a75 100644 --- a/daemon/src/ctrl_profiles/config.rs +++ b/daemon/src/ctrl_profiles/config.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config_traits::{StdConfig, StdConfigLoad}; use rog_profiles::fan_curve_set::FanCurveSet; -use rog_profiles::{FanCurveProfiles, Profile}; +use rog_profiles::Profile; use serde_derive::{Deserialize, Serialize}; use crate::CONFIG_PATH_BASE; @@ -36,40 +36,16 @@ impl StdConfigLoad for ProfileConfig {} #[derive(Deserialize, Serialize, Debug, Default)] pub struct FanCurveConfig { - balanced: FanCurveSet, - performance: FanCurveSet, - quiet: FanCurveSet, - #[serde(skip)] - device: FanCurveProfiles, -} - -impl FanCurveConfig { - pub fn update_device_config(&mut self) { - self.balanced = self.device.balanced.clone(); - self.performance = self.device.performance.clone(); - self.quiet = self.device.quiet.clone(); - } - - pub fn update_config(&mut self) { - self.balanced = self.device.balanced.clone(); - self.performance = self.device.performance.clone(); - self.quiet = self.device.quiet.clone(); - } - - pub fn device(&self) -> &FanCurveProfiles { - &self.device - } - - pub fn device_mut(&mut self) -> &mut FanCurveProfiles { - &mut self.device - } + pub balanced: FanCurveSet, + pub performance: FanCurveSet, + pub quiet: FanCurveSet, } impl StdConfig for FanCurveConfig { + /// Create a new config. The defaults are zeroed so the device must be read + /// to get the actual device defaults. fn new() -> Self { - let mut tmp = Self::default(); - tmp.update_device_config(); - tmp + Self::default() } fn config_dir() -> std::path::PathBuf { diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index ef15ceb3..b6752bc1 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -1,4 +1,4 @@ -use config_traits::StdConfig; +use config_traits::{StdConfig, StdConfigLoad}; use log::{info, warn}; use rog_platform::platform::AsusPlatform; use rog_platform::supported::PlatformProfileFunctions; @@ -9,9 +9,39 @@ use super::config::{FanCurveConfig, ProfileConfig}; use crate::error::RogError; use crate::GetSupported; +// TODO: macro wrapper for warn/info/error log macros to add module name +const MOD_NAME: &str = "CtrlPlatformProfile"; + +pub struct FanCurves { + config_file: FanCurveConfig, + profiles: FanCurveProfiles, +} + +impl FanCurves { + pub fn update_profiles_from_config(&mut self) { + self.profiles.balanced = self.config_file.balanced.clone(); + self.profiles.performance = self.config_file.performance.clone(); + self.profiles.quiet = self.config_file.quiet.clone(); + } + + pub fn update_config_from_profiles(&mut self) { + self.config_file.balanced = self.profiles.balanced.clone(); + self.config_file.performance = self.profiles.performance.clone(); + self.config_file.quiet = self.profiles.quiet.clone(); + } + + pub fn profiles(&self) -> &FanCurveProfiles { + &self.profiles + } + + pub fn profiles_mut(&mut self) -> &mut FanCurveProfiles { + &mut self.profiles + } +} + pub struct CtrlPlatformProfile { pub profile_config: ProfileConfig, - pub fan_config: Option, + pub fan_curves: Option, pub platform: AsusPlatform, } @@ -50,30 +80,50 @@ impl CtrlPlatformProfile { pub fn new(config: ProfileConfig) -> Result { let platform = AsusPlatform::new()?; if platform.has_platform_profile() || platform.has_throttle_thermal_policy() { - info!("Device has profile control available"); + info!("{MOD_NAME}: Device has profile control available"); let mut controller = CtrlPlatformProfile { profile_config: config, - fan_config: None, + fan_curves: None, platform, }; if FanCurveProfiles::get_device().is_ok() { - info!("Device has fan curves available"); - if controller.fan_config.is_none() { - controller.fan_config = Some(Default::default()); + info!("{MOD_NAME}: Device has fan curves available"); + let fan_config = FanCurveConfig::new(); + // Only do defaults if the config doesn't already exist + if !fan_config.file_path().exists() { + info!("{MOD_NAME}: Fetching default fan curves"); + controller.fan_curves = Some(FanCurves { + config_file: fan_config, + profiles: FanCurveProfiles::default(), + }); for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] { + // For each profile we need to switch to it before we + // can read the existing values from hardware. The ACPI method used + // for this is what limits us. controller.set_next_profile()?; + // Make sure to set the baseline to default controller.set_active_curve_to_defaults()?; - let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); - if let Some(curves) = controller.fan_config.as_ref() { + + if let Some(curves) = controller.fan_curves.as_ref() { info!( - "{active:?}: {}", - String::from(curves.device().get_fan_curves_for(active)) + "{MOD_NAME}: {active:?}: {}", + String::from(curves.profiles().get_fan_curves_for(active)) ); - curves.write(); } } + if let Some(curves) = controller.fan_curves.as_ref() { + curves.config_file.write(); + } + } else { + info!("{MOD_NAME}: Fan curves previously stored, loading..."); + let mut fan_curves = FanCurves { + config_file: fan_config.load(), + profiles: FanCurveProfiles::default(), + }; + fan_curves.update_profiles_from_config(); + controller.fan_curves = Some(fan_curves); } } @@ -85,9 +135,9 @@ impl CtrlPlatformProfile { pub fn save_config(&mut self) { self.profile_config.write(); - if let Some(fans) = self.fan_config.as_mut() { - fans.update_config(); - fans.write(); + if let Some(fans) = self.fan_curves.as_mut() { + fans.update_config_from_profiles(); + fans.config_file.write(); // config write } } @@ -115,9 +165,9 @@ impl CtrlPlatformProfile { /// Set the curve for the active profile active pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> { - if let Some(curves) = &mut self.fan_config { + if let Some(curves) = &mut self.fan_curves { if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.device_mut().write_profile_curve_to_platform( + curves.profiles_mut().write_profile_curve_to_platform( self.profile_config.active_profile, &mut device, )?; @@ -127,13 +177,13 @@ impl CtrlPlatformProfile { } pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> { - if let Some(curves) = self.fan_config.as_mut() { + if let Some(curves) = self.fan_curves.as_mut() { if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.device_mut().set_active_curve_to_defaults( + curves.profiles_mut().set_active_curve_to_defaults( self.profile_config.active_profile, &mut device, )?; - curves.update_config(); + curves.update_config_from_profiles(); } } Ok(()) diff --git a/daemon/src/ctrl_profiles/trait_impls.rs b/daemon/src/ctrl_profiles/trait_impls.rs index 84865e02..3eb02ae2 100644 --- a/daemon/src/ctrl_profiles/trait_impls.rs +++ b/daemon/src/ctrl_profiles/trait_impls.rs @@ -15,6 +15,8 @@ use super::controller::CtrlPlatformProfile; use crate::error::RogError; use crate::CtrlTask; +const MOD_NAME: &str = "ProfileZbus"; + const ZBUS_PATH: &str = "/org/asuslinux/Profile"; const UNSUPPORTED_MSG: &str = "Fan curves are not supported on this laptop or you require a patched kernel"; @@ -39,7 +41,7 @@ impl ProfileZbus { async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) { let mut ctrl = self.0.lock().await; ctrl.set_next_profile() - .unwrap_or_else(|err| warn!("{}", err)); + .unwrap_or_else(|err| warn!("{MOD_NAME}: {}", err)); ctrl.save_config(); Self::notify_profile(&ctxt, ctrl.profile_config.active_profile) @@ -64,11 +66,11 @@ impl ProfileZbus { // Read first just incase the user has modified the config before calling this ctrl.profile_config.read(); Profile::set_profile(profile) - .map_err(|e| warn!("set_profile, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) .ok(); ctrl.profile_config.active_profile = profile; ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)) .ok(); ctrl.save_config(); @@ -82,8 +84,8 @@ impl ProfileZbus { async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { let mut ctrl = self.0.lock().await; ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_config { - return Ok(curves.device().get_enabled_curve_profiles()); + if let Some(curves) = &mut ctrl.fan_curves { + return Ok(curves.profiles().get_enabled_curve_profiles()); } Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) } @@ -97,14 +99,13 @@ impl ProfileZbus { ) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_config { + if let Some(curves) = &mut ctrl.fan_curves { curves - .device_mut() + .profiles_mut() .set_profile_curve_enabled(profile, enabled); - curves.update_config(); ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)) .ok(); ctrl.save_config(); @@ -118,8 +119,8 @@ impl ProfileZbus { async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { let mut ctrl = self.0.lock().await; ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_config { - let curve = curves.device().get_fan_curves_for(profile); + if let Some(curves) = &mut ctrl.fan_curves { + let curve = curves.profiles().get_fan_curves_for(profile); return Ok(curve.clone()); } Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) @@ -130,17 +131,16 @@ impl ProfileZbus { async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_config { + if let Some(curves) = &mut ctrl.fan_curves { curves - .device_mut() + .profiles_mut() .save_fan_curve(curve, profile) .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - curves.update_config(); } else { return Err(Error::Failed(UNSUPPORTED_MSG.to_owned())); } ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("Profile::set_profile, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e)) .ok(); ctrl.save_config(); @@ -156,7 +156,7 @@ impl ProfileZbus { let mut ctrl = self.0.lock().await; ctrl.profile_config.read(); ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e)) .ok(); ctrl.save_config(); Ok(()) @@ -173,14 +173,14 @@ impl ProfileZbus { let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); Profile::set_profile(profile) - .map_err(|e| warn!("set_profile, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) .ok(); ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e)) .ok(); Profile::set_profile(active) - .map_err(|e| warn!("set_profile, {}", e)) + .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) .ok(); ctrl.save_config(); Ok(()) @@ -222,12 +222,12 @@ impl CtrlTask for ProfileZbus { let mut lock = ctrl.lock().await; if let Ok(profile) = lock.platform.get_throttle_thermal_policy().map_err(|e| { - error!("get_throttle_thermal_policy error: {e}"); + error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}"); }) { let new_profile = Profile::from_throttle_thermal_policy(profile); if new_profile != lock.profile_config.active_profile { - info!("platform_profile changed to {new_profile}"); + info!("{MOD_NAME}: platform_profile changed to {new_profile}"); lock.profile_config.active_profile = new_profile; lock.write_profile_curve_to_platform().unwrap(); lock.save_config(); @@ -262,7 +262,7 @@ impl CtrlTask for ProfileZbus { error!("Profile::from_str(&profile) error: {e}"); }) { if new_profile != lock.profile_config.active_profile { - info!("platform_profile changed to {new_profile}"); + info!("{MOD_NAME}: platform_profile changed to {new_profile}"); lock.profile_config.active_profile = new_profile; lock.write_profile_curve_to_platform().unwrap(); lock.save_config(); @@ -295,15 +295,14 @@ impl crate::Reloadable for ProfileZbus { async fn reload(&mut self) -> Result<(), RogError> { let mut ctrl = self.0.lock().await; let active = ctrl.profile_config.active_profile; - if let Some(curves) = &mut ctrl.fan_config { + if let Some(curves) = &mut ctrl.fan_curves { if let Ok(mut device) = FanCurveProfiles::get_device() { // There is a possibility that the curve was default zeroed, so this call // initialises the data from system read and we need to save it // after curves - .device_mut() + .profiles_mut() .write_profile_curve_to_platform(active, &mut device)?; - curves.update_config(); ctrl.profile_config.write(); } }