Skip to main content

openlogi_core/hid/
light.rs

1//! Semantic standalone-light commands — pure data, no I/O.
2//!
3//! The driver that encodes [`LightCommand`] into a device-specific raw HID
4//! report (e.g. Litra) lives in `openlogi_hid::write::litra`.
5
6use serde::{Deserialize, Serialize};
7
8use crate::config::LightSettings;
9use crate::device::LightCapabilities;
10
11/// A semantic command accepted by the standalone-light layer.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub enum LightCommand {
14    /// Turn the light on or off.
15    Power(bool),
16    /// Set normalized brightness from 0 to 100 percent.
17    BrightnessPercent(u8),
18    /// Set colour temperature in Kelvin.
19    TemperatureKelvin(u16),
20    /// Set brightness in the native unit advertised by the selected model.
21    /// This is primarily a diagnostic/CLI convenience; persisted settings
22    /// remain normalized percentages.
23    BrightnessNative(u16),
24}
25
26/// Expand protocol-neutral saved settings into only the controls advertised
27/// by a standalone light. Unsupported controls are omitted rather than sent
28/// speculatively, which keeps power-only and brightness-only drivers usable.
29#[must_use]
30pub fn commands_for_light_settings(
31    settings: LightSettings,
32    capabilities: LightCapabilities,
33) -> Vec<LightCommand> {
34    let mut commands = Vec::new();
35    if capabilities.power {
36        commands.push(LightCommand::Power(settings.enabled));
37    }
38    if capabilities.brightness.is_some() {
39        commands.push(LightCommand::BrightnessPercent(settings.brightness_percent));
40    }
41    if capabilities.temperature.is_some()
42        && let Some(kelvin) = settings.temperature_kelvin
43    {
44        commands.push(LightCommand::TemperatureKelvin(kelvin));
45    }
46    commands
47}
48
49#[cfg(test)]
50mod tests {
51    use super::{LightCommand, commands_for_light_settings};
52    use crate::config::LightSettings;
53    use crate::device::{LightCapabilities, LightValueRange, LightValueUnit};
54
55    #[test]
56    fn light_settings_expand_only_to_advertised_controls() {
57        let Ok(brightness) = LightValueRange::new(0, 100, 1, LightValueUnit::Percent) else {
58            panic!("valid brightness fixture");
59        };
60        let settings = LightSettings::new(false, 37, Some(4600));
61        let commands = commands_for_light_settings(
62            settings,
63            LightCapabilities {
64                brightness: Some(brightness),
65                ..LightCapabilities::default()
66            },
67        );
68
69        assert_eq!(commands, vec![LightCommand::BrightnessPercent(37)]);
70    }
71}