hidpp/feature/
brightness_control.rs1use openlogi_hidpp_derive::Feature;
4
5use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
6
7bitflags::bitflags! {
8 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
11 pub struct BrightnessCapabilities: u8 {
12 const HARDWARE_BRIGHTNESS = 1 << 0;
14 const EVENTS = 1 << 1;
16 const ILLUMINATION = 1 << 2;
18 const HARDWARE_ON_OFF = 1 << 3;
20 const TRANSIENT = 1 << 4;
22 }
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize))]
28#[non_exhaustive]
29pub struct BrightnessInfo {
30 pub min_brightness: u16,
32 pub max_brightness: u16,
34 pub steps: u16,
36 pub capabilities: BrightnessCapabilities,
38}
39
40#[derive(Clone, Feature)]
42#[creatable(id = 0x8040, version = 1)]
43pub struct BrightnessControlFeature {
44 endpoint: FeatureEndpoint,
46}
47
48impl BrightnessControlFeature {
49 pub async fn get_info(&self) -> Result<BrightnessInfo, Hidpp20Error> {
51 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
52 Ok(BrightnessInfo::from_payload(payload))
53 }
54
55 pub async fn get_brightness(&self) -> Result<u16, Hidpp20Error> {
57 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
58 Ok(u16::from_be_bytes([payload[0], payload[1]]))
59 }
60
61 pub async fn set_brightness(&self, brightness: u16) -> Result<(), Hidpp20Error> {
63 let [hi, lo] = brightness.to_be_bytes();
64 self.endpoint.call(2, [hi, lo, 0]).await?;
65 Ok(())
66 }
67
68 pub async fn get_illumination(&self) -> Result<bool, Hidpp20Error> {
70 Ok(self.endpoint.call(3, [0; 3]).await?.extend_payload()[0] & 1 != 0)
71 }
72
73 pub async fn set_illumination(&self, enabled: bool) -> Result<(), Hidpp20Error> {
75 self.endpoint.call(4, [u8::from(enabled), 0, 0]).await?;
76 Ok(())
77 }
78}
79
80impl BrightnessInfo {
81 fn from_payload(payload: [u8; 16]) -> Self {
82 Self {
83 min_brightness: u16::from_be_bytes([payload[4], payload[5]]),
84 max_brightness: u16::from_be_bytes([payload[0], payload[1]]),
85 steps: u16::from_be_bytes([payload[6], payload[2]]),
86 capabilities: BrightnessCapabilities::from_bits_retain(payload[3]),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::{BrightnessCapabilities, BrightnessInfo};
94
95 #[test]
96 fn parses_split_steps_field() {
97 let mut payload = [0; 16];
98 payload[0..=1].copy_from_slice(&1000u16.to_be_bytes());
99 payload[2] = 0x34;
100 payload[3] = BrightnessCapabilities::ILLUMINATION.bits();
101 payload[4..=5].copy_from_slice(&10u16.to_be_bytes());
102 payload[6] = 0x12;
103
104 let info = BrightnessInfo::from_payload(payload);
105
106 assert_eq!(info.min_brightness, 10);
107 assert_eq!(info.max_brightness, 1000);
108 assert_eq!(info.steps, 0x1234);
109 assert!(
110 info.capabilities
111 .contains(BrightnessCapabilities::ILLUMINATION)
112 );
113 }
114}