hyperion/api/
types.rs

1use palette::{
2    encoding::{Linear, Srgb},
3    FromColor, Hsl,
4};
5use serde::Serialize;
6
7use crate::{
8    component::ComponentName,
9    global::{InputMessageData, Message},
10    models::Color,
11};
12
13fn not_positive(x: &i64) -> bool {
14    *x <= 0
15}
16
17fn color_to_hsl(color: Color) -> Hsl<Linear<Srgb>> {
18    let (r, g, b) = color.into_components();
19    palette::Hsl::from_color(palette::LinSrgb::new(
20        r as f32 / 255.0,
21        g as f32 / 255.0,
22        b as f32 / 255.0,
23    ))
24}
25
26#[derive(Debug, Serialize)]
27pub struct PriorityInfo {
28    pub priority: i32,
29    #[serde(skip_serializing_if = "not_positive")]
30    pub duration_ms: i64,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub owner: Option<String>,
33    pub component_id: ComponentName,
34    pub origin: String,
35    pub active: bool,
36    pub visible: bool,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub value: Option<LedColor>,
39}
40
41impl PriorityInfo {
42    pub fn new(
43        msg: &crate::global::InputMessage,
44        origin: String,
45        expires: Option<std::time::Instant>,
46        visible: bool,
47    ) -> Self {
48        let duration_ms = expires
49            .and_then(|when| {
50                let now = std::time::Instant::now();
51
52                if when > now {
53                    chrono::Duration::from_std(when - now).ok()
54                } else {
55                    Some(chrono::Duration::zero())
56                }
57            })
58            .map(|d| d.num_milliseconds())
59            .unwrap_or(-1);
60        let active = duration_ms >= -1;
61
62        match msg.data() {
63            InputMessageData::SolidColor {
64                priority, color, ..
65            } => Self {
66                priority: *priority,
67                duration_ms,
68                owner: None,
69                component_id: msg.component(),
70                origin,
71                active,
72                visible,
73                value: Some(color.into()),
74            },
75            InputMessageData::Image { priority, .. }
76            | InputMessageData::LedColors { priority, .. }
77            | InputMessageData::Effect { priority, .. } => Self {
78                priority: *priority,
79                duration_ms,
80                owner: None,
81                component_id: msg.component(),
82                origin,
83                active,
84                visible,
85                value: None,
86            },
87            InputMessageData::Clear { .. } | InputMessageData::ClearAll => {
88                panic!("cannot create PriorityInfo for InputMessage")
89            }
90        }
91    }
92}
93
94#[derive(Debug, Serialize)]
95#[serde(rename_all = "UPPERCASE")]
96pub struct LedColor {
97    pub rgb: [u8; 3],
98    pub hsl: (u16, f32, f32),
99}
100
101impl From<&Color> for LedColor {
102    fn from(c: &Color) -> Self {
103        let hsl = color_to_hsl(*c);
104
105        Self {
106            rgb: [c.red, c.green, c.blue],
107            hsl: (
108                (hsl.hue.into_positive_degrees() * 100.) as u16,
109                hsl.saturation,
110                hsl.lightness,
111            ),
112        }
113    }
114}
115
116pub fn i32_to_duration(d: Option<i32>) -> Option<chrono::Duration> {
117    if let Some(d) = d {
118        if d <= 0 {
119            None
120        } else {
121            Some(chrono::Duration::milliseconds(d as _))
122        }
123    } else {
124        None
125    }
126}