Files
lighter/src/bulb.rs
2022-07-05 14:20:30 +02:00

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))
}