121 lines
2.9 KiB
Rust
121 lines
2.9 KiB
Rust
use std::str::FromStr;
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct BulbMode {
|
|
pub power: bool,
|
|
pub color: BulbColor,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum BulbColor {
|
|
/// Light temperature, brightness
|
|
Kelvin { t: f32, b: f32 },
|
|
|
|
/// Hue, Saturation, Brightness
|
|
HSB { h: f32, s: f32, b: f32 },
|
|
}
|
|
|
|
impl FromStr for BulbColor {
|
|
type Err = anyhow::Error;
|
|
fn from_str(s: &str) -> anyhow::Result<Self> {
|
|
let parsed = u64::from_str_radix(s, 16)?;
|
|
|
|
let to_f32 = |byte: u8| byte as f32 / 255.;
|
|
let [.., red, green, blue, white, warm] = parsed.to_be_bytes().map(to_f32);
|
|
|
|
if (red + green + blue) != 0. {
|
|
let (h, s, b) = rgb_to_hsb(red, green, blue);
|
|
Ok(BulbColor::hsb(h, s, b))
|
|
} else {
|
|
let b = warm + white;
|
|
let t = white / b;
|
|
|
|
Ok(BulbColor::kelvin(t, b))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BulbColor {
|
|
pub fn color_string(self) -> String {
|
|
let [mut red, mut green, mut blue, mut white, mut warm] = [0u8; 5];
|
|
|
|
match self {
|
|
BulbColor::HSB { h, s, b } => (red, green, blue) = hsb_to_rgb(h, s, b),
|
|
BulbColor::Kelvin { t, b } => {
|
|
(white, warm) = ((t * b * 255.0) as u8, ((1. - t) * b * 255.0) as u8)
|
|
}
|
|
}
|
|
|
|
let s = format!("{red:02x}{green:02x}{blue:02x}{white:02x}{warm:02x}");
|
|
|
|
s
|
|
}
|
|
}
|
|
|
|
impl BulbColor {
|
|
pub fn hsb(h: f32, s: f32, b: f32) -> Self {
|
|
BulbColor::HSB {
|
|
h: h.clamp(0.0, 1.0),
|
|
s: s.clamp(0.0, 1.0),
|
|
b: b.clamp(0.0, 1.0),
|
|
}
|
|
}
|
|
|
|
pub fn kelvin(t: f32, b: f32) -> Self {
|
|
BulbColor::Kelvin {
|
|
t: t.clamp(0.0, 1.0),
|
|
b: b.clamp(0.0, 1.0),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for BulbColor {
|
|
fn default() -> Self {
|
|
BulbColor::Kelvin { t: 0.0, b: 0.0 }
|
|
}
|
|
}
|
|
|
|
fn rgb_to_hsb(red: f32, green: f32, blue: f32) -> (f32, f32, f32) {
|
|
let x_max = red.max(green).max(blue);
|
|
let x_min = red.min(green).min(blue);
|
|
|
|
let b = x_max;
|
|
let c = x_max - x_min;
|
|
|
|
let h = match b {
|
|
_ if c == 0. => 0.,
|
|
_ if b == red => (green - blue) / c,
|
|
_ if b == green => 2. + (blue - red) / c,
|
|
_ => 4. + (red - green) / c,
|
|
};
|
|
let mut h = h * 60.0 / 360.0;
|
|
if h < 0.0 {
|
|
h += 1.0;
|
|
}
|
|
|
|
let s = if b == 0.0 { 0.0 } else { c / b };
|
|
|
|
(h, s, b)
|
|
}
|
|
|
|
fn hsb_to_rgb(h: f32, s: f32, b: f32) -> (u8, u8, u8) {
|
|
let h = 360.0 * h.clamp(0.0, 1.0);
|
|
|
|
let c = b * s; // chroma
|
|
|
|
let x = c * (1. - ((h / 60.) % 2. - 1.).abs());
|
|
let m = b - c;
|
|
let m = |v| ((v + m) * 255.0) as u8;
|
|
|
|
let (r, g, b) = match h {
|
|
_ if h < 60. => (c, x, 0.0),
|
|
_ if h < 120.0 => (x, c, 0.0),
|
|
_ if h < 180.0 => (0.0, c, x),
|
|
_ if h < 240.0 => (0.0, x, c),
|
|
_ if h < 300.0 => (x, 0.0, c),
|
|
_ => (c, 0.0, x),
|
|
};
|
|
|
|
(m(r), m(g), m(b))
|
|
}
|