refactor: sensor configuration fields

Automatically round decimal values for sensor.x and .y to an integer.
Make fields optional which are not used in every sensor.
This commit is contained in:
Markus Zehnder
2025-08-24 21:18:56 +02:00
parent 98941a00fe
commit 84455e9254
6 changed files with 50 additions and 62 deletions
-2
View File
@@ -53,7 +53,6 @@ The background image and sensor definitions are taken from the default system pa
"sensor": [ "sensor": [
{ {
"mode": 1, "mode": 1,
"type": 1,
"name": "CPU temp", "name": "CPU temp",
"label": "cpu_temperature", "label": "cpu_temperature",
"x": 195, "x": 195,
@@ -67,7 +66,6 @@ The background image and sensor definitions are taken from the default system pa
}, },
{ {
"mode": 1, "mode": 1,
"type": 1,
"name": "CPU usage", "name": "CPU usage",
"label": "cpu_percent", "label": "cpu_percent",
"unit": "%", "unit": "%",
-5
View File
@@ -31,7 +31,6 @@ Example `panel.json` with a single "fan" indicator sensor and the following (par
"id": "29d9ef2d-30b4-459d-b2b0-43cb6d4d6b41", "id": "29d9ef2d-30b4-459d-b2b0-43cb6d4d6b41",
"itemName": "CPU usage", "itemName": "CPU usage",
"mode": 2, "mode": 2,
"type": 1,
"direction": 1, "direction": 1,
"label": "cpu_percent", "label": "cpu_percent",
"value": "47.7", "value": "47.7",
@@ -39,10 +38,6 @@ Example `panel.json` with a single "fan" indicator sensor and the following (par
"y": 184, "y": 184,
"width": 237, "width": 237,
"height": 237, "height": 237,
"fontColor": "#ffffff",
"fontSize": 14,
"fontFamily": "default_font",
"textAlign": "left",
"minAngle": -160, "minAngle": -160,
"maxAngle": 30, "maxAngle": 30,
"minValue": 0, "minValue": 0,
-14
View File
@@ -25,41 +25,30 @@ The background image and sensor definitions are taken from the default system pa
"sensor": [ "sensor": [
{ {
"mode": 3, "mode": 3,
"type": 2,
"name": "SSD 4 usage", "name": "SSD 4 usage",
"label": "storage_ssd4_usage", "label": "storage_ssd4_usage",
"x": 400, "x": 400,
"y": 45, "y": 45,
"direction": 1, "direction": 1,
"value": "35", "value": "35",
"fontFamily": "HarmonyOS_Sans_SC_Bold",
"fontSize": 24,
"fontColor": -1,
"textAlign": "center",
"minValue": 0, "minValue": 0,
"maxValue": 100, "maxValue": 100,
"pic": "progress.png" "pic": "progress.png"
}, },
{ {
"mode": 3, "mode": 3,
"type": 2,
"name": "SSD 5 usage", "name": "SSD 5 usage",
"label": "storage_ssd5_usage", "label": "storage_ssd5_usage",
"x": 400, "x": 400,
"y": 106, "y": 106,
"direction": 1, "direction": 1,
"value": "80", "value": "80",
"fontFamily": "HarmonyOS_Sans_SC_Bold",
"fontSize": 24,
"fontColor": -1,
"textAlign": "center",
"minValue": 0, "minValue": 0,
"maxValue": 100, "maxValue": 100,
"pic": "progress.png" "pic": "progress.png"
}, },
{ {
"mode": 1, "mode": 1,
"type": 2,
"name": "SSD 4 temp", "name": "SSD 4 temp",
"label": "storage_ssd4_temperature", "label": "storage_ssd4_temperature",
"x": 580, "x": 580,
@@ -68,7 +57,6 @@ The background image and sensor definitions are taken from the default system pa
"value": "34", "value": "34",
"fontFamily": "HarmonyOS_Sans_SC_Bold", "fontFamily": "HarmonyOS_Sans_SC_Bold",
"fontSize": 24, "fontSize": 24,
"fontColor": -1,
"textAlign": "center", "textAlign": "center",
"integerDigits": -1, "integerDigits": -1,
"decimalDigits": 0, "decimalDigits": 0,
@@ -76,7 +64,6 @@ The background image and sensor definitions are taken from the default system pa
}, },
{ {
"mode": 1, "mode": 1,
"type": 2,
"name": "SSD 5 temp", "name": "SSD 5 temp",
"label": "storage_ssd5_temperature", "label": "storage_ssd5_temperature",
"x": 580, "x": 580,
@@ -85,7 +72,6 @@ The background image and sensor definitions are taken from the default system pa
"value": "35", "value": "35",
"fontFamily": "HarmonyOS_Sans_SC_Bold", "fontFamily": "HarmonyOS_Sans_SC_Bold",
"fontSize": 24, "fontSize": 24,
"fontColor": -1,
"textAlign": "center", "textAlign": "center",
"integerDigits": -1, "integerDigits": -1,
"decimalDigits": 0, "decimalDigits": 0,
-6
View File
@@ -31,7 +31,6 @@ Example `panel.json` with a single "pointer" indicator sensor and the following
"id": "a9d4acac-2af9-4fe0-9f69-86cd09f25696", "id": "a9d4acac-2af9-4fe0-9f69-86cd09f25696",
"itemName": "CPU dial", "itemName": "CPU dial",
"mode": 4, "mode": 4,
"type": 1,
"direction": 1, "direction": 1,
"label": "cpu_percent", "label": "cpu_percent",
"value": "47.7", "value": "47.7",
@@ -39,11 +38,6 @@ Example `panel.json` with a single "pointer" indicator sensor and the following
"y": 208, "y": 208,
"width": 302, "width": 302,
"height": 302, "height": 302,
"fontColor": "#ffffff",
"fontSize": 14,
"fontFamily": "",
"fontWeight": "normal",
"textAlign": "left",
"minAngle": -110, "minAngle": -110,
"maxAngle": 110, "maxAngle": 110,
"minValue": 0, "minValue": 0,
+30 -17
View File
@@ -91,8 +91,11 @@ pub fn load_custom_panel<P: AsRef<Path>>(path: P) -> anyhow::Result<Panel> {
{ {
sensor.pic = Some(img_path.join(pic).display().to_string()); sensor.pic = Some(img_path.join(pic).display().to_string());
} }
if !sensor.font_family.is_empty() && !Path::new(&sensor.font_family).is_absolute() { if let Some(font_family) = &sensor.font_family
sensor.font_family = font_path.join(&sensor.font_family).display().to_string(); && !font_family.is_empty()
&& !Path::new(&font_family).is_absolute()
{
sensor.font_family = Some(font_path.join(font_family).display().to_string());
} }
} }
@@ -283,7 +286,7 @@ impl Panel {
pub struct Sensor { pub struct Sensor {
/// Sensor mode: text, fan, progress, pointer /// Sensor mode: text, fan, progress, pointer
pub mode: SensorMode, pub mode: SensorMode,
/// Sensor type. TODO verify sensor type values /// Sensor type, _not used_.
/// - 1 Time / Date Labels /// - 1 Time / Date Labels
/// - 2 Windows-specific system info /// - 2 Windows-specific system info
/// - 3 Hardware value /// - 3 Hardware value
@@ -293,7 +296,7 @@ pub struct Sensor {
/// - 7 system info ? /// - 7 system info ?
/// - 8 lm-sensor ? /// - 8 lm-sensor ?
#[serde(rename = "type")] #[serde(rename = "type")]
pub sensor_type: i32, pub sensor_type: Option<i32>,
/// Label name for internal panels. /// Label name for internal panels.
pub name: Option<String>, pub name: Option<String>,
/// Label name for custom panels. /// Label name for custom panels.
@@ -312,12 +315,12 @@ pub struct Sensor {
/// Optional unit text to print after the value /// Optional unit text to print after the value
#[serde(deserialize_with = "empty_string_as_none")] #[serde(deserialize_with = "empty_string_as_none")]
pub unit: Option<String>, pub unit: Option<String>,
/// x-position. Custom panel coordinates are stored as float! /// Rounded x-position. Custom panel coordinates are stored as float!
// TODO use i32 and round from f32 in deserialization #[serde(deserialize_with = "f32_as_rounded_i32")]
pub x: f32, pub x: i32,
/// y-position. /// Rounded y-position. Custom panel coordinates are stored as float!
// TODO use i32 and round from f32 in deserialization #[serde(deserialize_with = "f32_as_rounded_i32")]
pub y: f32, pub y: i32,
/// Used for pointer type /// Used for pointer type
pub width: Option<i32>, pub width: Option<i32>,
/// Used for pointer type /// Used for pointer type
@@ -326,14 +329,14 @@ pub struct Sensor {
pub direction: Option<SensorDirection>, pub direction: Option<SensorDirection>,
/// Font name matching font filename without file extension. /// Font name matching font filename without file extension.
pub font_family: String, pub font_family: Option<String>,
/// TODO font size unit: points or pixels? /// TODO font size unit: points or pixels?
pub font_size: i32, pub font_size: Option<i32>,
/// Font color in `#RRGGBB` notation, or -1 if not set. #ffffff = white, #ff0000 = red /// Font color in `#RRGGBB` notation, or -1 if not set. #ffffff = white, #ff0000 = red
pub font_color: FontColor, pub font_color: Option<FontColor>,
/// _Not (yet) used_ /// _Not (yet) used_
pub font_weight: FontWeight, pub font_weight: Option<FontWeight>,
pub text_align: TextAlign, pub text_align: Option<TextAlign>,
/// Number of integer places for the sensor value. /// Number of integer places for the sensor value.
// -1 ≈ unset ⇒ Option<i32> // -1 ≈ unset ⇒ Option<i32>
@@ -429,16 +432,18 @@ pub enum TimeDateLabel {
HM3, HM3,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum FontWeight { pub enum FontWeight {
#[default]
Normal, Normal,
Bold, Bold,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum TextAlign { pub enum TextAlign {
#[default]
Left, Left,
Center, Center,
Right, Right,
@@ -569,3 +574,11 @@ where
let option = Option::<String>::deserialize(deserializer)?; let option = Option::<String>::deserialize(deserializer)?;
Ok(option.and_then(|s| if s.trim().is_empty() { None } else { Some(s) })) Ok(option.and_then(|s| if s.trim().is_empty() { None } else { Some(s) }))
} }
fn f32_as_rounded_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
where
D: Deserializer<'de>,
{
let rounded = f32::deserialize(deserializer).map(f32::round)?;
Ok(rounded as i32)
}
+20 -18
View File
@@ -205,15 +205,16 @@ impl PanelRenderer {
value: &str, value: &str,
unit: &str, unit: &str,
) -> Result<(), ImageProcessingError> { ) -> Result<(), ImageProcessingError> {
let font = self let font = if let Some(font_family) = &sensor.font_family {
.font_handler self.font_handler.get_ttf_font_or_default(font_family)
.get_ttf_font_or_default(&sensor.font_family); } else {
FontHandler::default_font()
};
let font_size = sensor.font_size.unwrap_or(14) as f32;
// TODO verify pixel scaling! Is font_size point size or pixel size? // TODO verify pixel scaling! Is font_size point size or pixel size?
// This is still a bit off compared to the original AOOSTAR-X. Only tested with HarmonyOS_Sans_SC_Bold! // This is still a bit off compared to the original AOOSTAR-X. Only tested with HarmonyOS_Sans_SC_Bold!
let adjustment_hack = 0.7; let adjustment_hack = 0.7;
let scale = font let scale = font.pt_to_px_scale(font_size * adjustment_hack).unwrap();
.pt_to_px_scale(sensor.font_size as f32 * adjustment_hack)
.unwrap();
let text = format_value( let text = format_value(
value, value,
@@ -223,12 +224,12 @@ impl PanelRenderer {
); );
let size = text_size(scale, &font, &text); let size = text_size(scale, &font, &text);
// TODO verify x & y-coordinate handling // TODO verify x & y-coordinate handling
let x = match sensor.text_align { let x = match sensor.text_align.unwrap_or_default() {
TextAlign::Left => sensor.x as i32, TextAlign::Left => sensor.x,
TextAlign::Center => sensor.x as i32 - (size.0 / 2) as i32, TextAlign::Center => sensor.x - (size.0 / 2) as i32,
TextAlign::Right => sensor.x as i32 - size.0 as i32, TextAlign::Right => sensor.x - size.0 as i32,
}; };
let y = (sensor.y - scale.y / 2f32) as i32; let y = (sensor.y as f32 - scale.y / 2f32) as i32;
// let y = sensor.y as i32 - (size.1 / 2) as i32; // let y = sensor.y as i32 - (size.1 / 2) as i32;
debug!( debug!(
@@ -236,7 +237,7 @@ impl PanelRenderer {
sensor.x, sensor.y sensor.x, sensor.y
); );
let font_color = sensor.font_color.into(); let font_color = sensor.font_color.unwrap_or_default().into();
draw_text_mut(background, font_color, x, y, scale, &font, &text); draw_text_mut(background, font_color, x, y, scale, &font, &text);
Ok(()) Ok(())
@@ -257,8 +258,8 @@ impl PanelRenderer {
return Err(ImageProcessingError::InvalidDirection(direction)); return Err(ImageProcessingError::InvalidDirection(direction));
} }
let pos_x = sensor.x as i32; let pos_x = sensor.x;
let pos_y = sensor.y as i32; let pos_y = sensor.y;
let pic_path = sensor.pic.as_ref().ok_or_else(|| { let pic_path = sensor.pic.as_ref().ok_or_else(|| {
ImageProcessingError::ImageLoadError("No picture specified".to_string()) ImageProcessingError::ImageLoadError("No picture specified".to_string())
@@ -385,8 +386,8 @@ impl PanelRenderer {
} }
} }
let pos_x = sensor.x as i32; let pos_x = sensor.x;
let pos_y = sensor.y as i32; let pos_y = sensor.y;
if let Some(progress_layer) = self.get_layer(SensorMode::Progress) { if let Some(progress_layer) = self.get_layer(SensorMode::Progress) {
PanelRenderer::paste_image(progress_layer, &processed_img, pos_x, pos_y); PanelRenderer::paste_image(progress_layer, &processed_img, pos_x, pos_y);
@@ -420,8 +421,8 @@ impl PanelRenderer {
return Err(ImageProcessingError::InvalidDirection(direction)); return Err(ImageProcessingError::InvalidDirection(direction));
} }
let x_center = sensor.x as i32; let x_center = sensor.x;
let y_center = sensor.y as i32; let y_center = sensor.y;
let xz_x = sensor.xz_x.unwrap_or(0); let xz_x = sensor.xz_x.unwrap_or(0);
let xz_y = sensor.xz_y.unwrap_or(0); let xz_y = sensor.xz_y.unwrap_or(0);
@@ -429,6 +430,7 @@ impl PanelRenderer {
ImageProcessingError::ImageLoadError("No picture specified".to_string()) ImageProcessingError::ImageLoadError("No picture specified".to_string())
})?; })?;
// TODO combine get image with resize
let mut pic = self let mut pic = self
.image_cache .image_cache
.get(pic_path, None) .get(pic_path, None)