hidpp/feature/brightness_control/
mod.rs1use std::sync::Arc;
4
5use crate::{
6 channel::HidppChannel,
7 feature::{CreatableFeature, Feature, FeatureEndpoint},
8 protocol::v20::Hidpp20Error,
9};
10
11bitflags::bitflags! {
12 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
15 pub struct BrightnessCapabilities: u8 {
16 const HARDWARE_BRIGHTNESS = 1 << 0;
18 const EVENTS = 1 << 1;
20 const ILLUMINATION = 1 << 2;
22 const HARDWARE_ON_OFF = 1 << 3;
24 const TRANSIENT = 1 << 4;
26 }
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32#[non_exhaustive]
33pub struct BrightnessInfo {
34 pub min_brightness: u16,
36 pub max_brightness: u16,
38 pub steps: u16,
40 pub capabilities: BrightnessCapabilities,
42}
43
44#[derive(Clone)]
46pub struct BrightnessControlFeature {
47 endpoint: FeatureEndpoint,
49}
50
51impl CreatableFeature for BrightnessControlFeature {
52 const ID: u16 = 0x8040;
53 const STARTING_VERSION: u8 = 1;
54
55 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
56 Self {
57 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
58 }
59 }
60}
61
62impl Feature for BrightnessControlFeature {}
63
64impl BrightnessControlFeature {
65 pub async fn get_info(&self) -> Result<BrightnessInfo, Hidpp20Error> {
67 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
68 Ok(BrightnessInfo::from_payload(payload))
69 }
70
71 pub async fn get_brightness(&self) -> Result<u16, Hidpp20Error> {
73 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
74 Ok(u16::from_be_bytes([payload[0], payload[1]]))
75 }
76
77 pub async fn set_brightness(&self, brightness: u16) -> Result<(), Hidpp20Error> {
79 let [hi, lo] = brightness.to_be_bytes();
80 self.endpoint.call(2, [hi, lo, 0]).await?;
81 Ok(())
82 }
83
84 pub async fn get_illumination(&self) -> Result<bool, Hidpp20Error> {
86 Ok(self.endpoint.call(3, [0; 3]).await?.extend_payload()[0] & 1 != 0)
87 }
88
89 pub async fn set_illumination(&self, enabled: bool) -> Result<(), Hidpp20Error> {
91 self.endpoint.call(4, [u8::from(enabled), 0, 0]).await?;
92 Ok(())
93 }
94}
95
96impl BrightnessInfo {
97 fn from_payload(payload: [u8; 16]) -> Self {
98 Self {
99 min_brightness: u16::from_be_bytes([payload[4], payload[5]]),
100 max_brightness: u16::from_be_bytes([payload[0], payload[1]]),
101 steps: u16::from_be_bytes([payload[6], payload[2]]),
102 capabilities: BrightnessCapabilities::from_bits_retain(payload[3]),
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::{BrightnessCapabilities, BrightnessInfo};
110
111 #[test]
112 fn parses_split_steps_field() {
113 let mut payload = [0; 16];
114 payload[0..=1].copy_from_slice(&1000u16.to_be_bytes());
115 payload[2] = 0x34;
116 payload[3] = BrightnessCapabilities::ILLUMINATION.bits();
117 payload[4..=5].copy_from_slice(&10u16.to_be_bytes());
118 payload[6] = 0x12;
119
120 let info = BrightnessInfo::from_payload(payload);
121
122 assert_eq!(info.min_brightness, 10);
123 assert_eq!(info.max_brightness, 1000);
124 assert_eq!(info.steps, 0x1234);
125 assert!(
126 info.capabilities
127 .contains(BrightnessCapabilities::ILLUMINATION)
128 );
129 }
130}