1use ftswarm_macros::{default_new_swarm_object_impls, impl_swarm_object};
2use ftswarm_proto::command::argument::Argument;
3use ftswarm_proto::command::rpc::RpcFunction;
4use crate::FtSwarm;
5use crate::swarm_object::{NewSwarmObject, SwarmObject, Updateable};
6
7#[derive(Updateable, Clone)]
8pub struct Led {
9 pub name: String,
10 swarm: FtSwarm
11}
12
13impl_swarm_object!(Led, ());
14
15impl NewSwarmObject<()> for Led {
16 fn new(name: &str, swarm: FtSwarm, _params: ()) -> Box<Self> {
17 Box::new(Led {
18 name: name.to_string(),
19 swarm
20 })
21 }
22
23 default_new_swarm_object_impls!();
24}
25
26#[derive(Clone, Debug, PartialEq, Eq, Hash)]
27pub struct LedColor {
28 pub red: i32,
29 pub green: i32,
30 pub blue: i32
31}
32
33impl LedColor {
34 pub fn new(red: i32, green: i32, blue: i32) -> Self {
35 LedColor {
36 red,
37 green,
38 blue
39 }
40 }
41
42 pub fn rgb(red: i32, green: i32, blue: i32) -> LedColor {
43 LedColor::new(red, green, blue)
44 }
45
46 pub fn hsl(hue: i32, saturation: i32, lightness: i32) -> LedColor {
47 let hue = hue as f64 / 360.0;
48 let saturation = saturation as f64 / 100.0;
49 let lightness = lightness as f64 / 100.0;
50
51 let c = (1.0 - (2.0 * lightness - 1.0).abs()) * saturation;
52 let x = c * (1.0 - ((hue * 6.0) % 2.0 - 1.0).abs());
53 let m = lightness - c / 2.0;
54
55 let (red, green, blue) = if hue < 1.0 / 6.0 {
56 (c, x, 0.0)
57 } else if hue < 2.0 / 6.0 {
58 (x, c, 0.0)
59 } else if hue < 3.0 / 6.0 {
60 (0.0, c, x)
61 } else if hue < 4.0 / 6.0 {
62 (0.0, x, c)
63 } else if hue < 5.0 / 6.0 {
64 (x, 0.0, c)
65 } else {
66 (c, 0.0, x)
67 };
68
69 LedColor::new(
70 ((red + m) * 255.0) as i32,
71 ((green + m) * 255.0) as i32,
72 ((blue + m) * 255.0) as i32
73 )
74 }
75
76 pub fn red() -> LedColor {
77 LedColor::new(255, 0, 0)
78 }
79
80 pub fn green() -> LedColor {
81 LedColor::new(0, 255, 0)
82 }
83
84 pub fn blue() -> LedColor {
85 LedColor::new(0, 0, 255)
86 }
87
88 pub fn yellow() -> LedColor {
89 LedColor::new(255, 255, 0)
90 }
91
92 pub fn cyan() -> LedColor {
93 LedColor::new(0, 255, 255)
94 }
95
96 pub fn magenta() -> LedColor {
97 LedColor::new(255, 0, 255)
98 }
99
100 pub fn white() -> LedColor {
101 LedColor::new(255, 255, 255)
102 }
103
104 pub fn off() -> LedColor {
105 LedColor::new(0, 0, 0)
106 }
107}
108
109impl From<String> for LedColor {
110 fn from(value: String) -> Self {
111 if value.starts_with("#") {
114 let hex = value.trim_start_matches("#");
115 let red = i32::from_str_radix(&hex[0..2], 16).unwrap();
116 let green = i32::from_str_radix(&hex[2..4], 16).unwrap();
117 let blue = i32::from_str_radix(&hex[4..6], 16).unwrap();
118 return LedColor::new(red, green, blue);
119 }
120
121 let mut parts = value.split(",");
123 if let (Some(red), Some(green), Some(blue)) = (parts.next(), parts.next(), parts.next()) {
124 return LedColor::new(red.parse().unwrap(), green.parse().unwrap(), blue.parse().unwrap());
125 }
126
127 match value.to_lowercase().as_str() {
129 "red" => LedColor::new(255, 0, 0),
130 "green" => LedColor::new(0, 255, 0),
131 "blue" => LedColor::new(0, 0, 255),
132 "yellow" => LedColor::new(255, 255, 0),
133 "cyan" => LedColor::new(0, 255, 255),
134 "magenta" => LedColor::new(255, 0, 255),
135 "white" => LedColor::new(255, 255, 255),
136 "black" => LedColor::new(0, 0, 0),
137 _ => panic!("Invalid color")
138 }
139 }
140}
141
142impl Into<i64> for LedColor {
143 fn into(self) -> i64 {
144 ((self.red << 16) | (self.green << 8) | self.blue) as i64
145 }
146}
147
148impl Led {
149 pub async fn set_color(&self, color: LedColor) -> Result<(), String> {
150 self.run_command(RpcFunction::SetColor, vec![Argument::Int(color.into())]).await
151 .map(|_| ())
152 }
153
154 pub async fn set_brightness(&self, brightness: i32) -> Result<(), String> {
155 let brightness = brightness.min(255).max(0);
156 self.run_command(RpcFunction::SetBrightness, vec![Argument::Int(brightness as i64)]).await
157 .map(|_| ())
158 }
159}