hidpp/feature/illumination/
mod.rs1pub mod event;
13pub mod types;
14
15#[cfg(test)]
16mod tests;
17
18use std::sync::Arc;
19
20pub use event::IlluminationEvent;
21pub use types::{
22 BrightnessClampedSource, ControlCapabilities, ControlInfo, IlluminationState, LevelConfig,
23 SetLevels,
24};
25
26use self::types::{be16, illumination_state};
27use crate::{
28 channel::{HidppChannel, MessageListenerGuard},
29 event::EventEmitter,
30 feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
31 protocol::v20::{ErrorType, Hidpp20Error},
32};
33
34const FN_GET_ILLUMINATION: u8 = 0;
37const FN_SET_ILLUMINATION: u8 = 1;
38const FN_GET_BRIGHTNESS_INFO: u8 = 2;
39const FN_GET_BRIGHTNESS: u8 = 3;
40const FN_SET_BRIGHTNESS: u8 = 4;
41const FN_GET_BRIGHTNESS_LEVELS: u8 = 5;
42const FN_SET_BRIGHTNESS_LEVELS: u8 = 6;
43const FN_GET_COLOR_TEMPERATURE_INFO: u8 = 7;
44const FN_GET_COLOR_TEMPERATURE: u8 = 8;
45const FN_SET_COLOR_TEMPERATURE: u8 = 9;
46const FN_GET_COLOR_TEMPERATURE_LEVELS: u8 = 10;
47const FN_SET_COLOR_TEMPERATURE_LEVELS: u8 = 11;
48const FN_GET_BRIGHTNESS_EFFECTIVE_MAX: u8 = 12;
49
50pub struct IlluminationFeature {
52 endpoint: FeatureEndpoint,
54
55 emitter: Arc<EventEmitter<IlluminationEvent>>,
57
58 _msg_listener: MessageListenerGuard,
60}
61
62impl CreatableFeature for IlluminationFeature {
63 const ID: u16 = 0x1990;
64 const STARTING_VERSION: u8 = 0;
65
66 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
67 let emitter = Arc::new(EventEmitter::new());
68
69 let listener = chan.add_msg_listener_guarded({
70 let emitter = Arc::clone(&emitter);
71
72 move |raw, matched| {
73 let Some((func, payload)) =
74 event_payload(raw, matched, device_index, feature_index)
75 else {
76 return;
77 };
78 if let Some(event) = event::decode_event(func.to_lo(), &payload) {
79 emitter.emit(event);
80 }
81 }
82 });
83
84 Self {
85 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
86 emitter,
87 _msg_listener: listener,
88 }
89 }
90}
91
92impl Feature for IlluminationFeature {}
93
94impl EmittingFeature<IlluminationEvent> for IlluminationFeature {
95 fn listen(&self) -> async_channel::Receiver<IlluminationEvent> {
96 self.emitter.create_receiver()
97 }
98}
99
100impl IlluminationFeature {
101 pub async fn get_illumination(&self) -> Result<IlluminationState, Hidpp20Error> {
103 let payload = self
104 .endpoint
105 .call(FN_GET_ILLUMINATION, [0; 3])
106 .await?
107 .extend_payload();
108 illumination_state(payload[0])
109 }
110
111 pub async fn set_illumination(&self, state: IlluminationState) -> Result<(), Hidpp20Error> {
113 self.endpoint
114 .call(FN_SET_ILLUMINATION, [u8::from(state), 0, 0])
115 .await?;
116 Ok(())
117 }
118
119 pub async fn get_brightness_info(&self) -> Result<ControlInfo, Hidpp20Error> {
121 self.read_info(FN_GET_BRIGHTNESS_INFO).await
122 }
123
124 pub async fn get_brightness(&self) -> Result<u16, Hidpp20Error> {
126 self.read_value(FN_GET_BRIGHTNESS).await
127 }
128
129 pub async fn set_brightness(&self, brightness: u16) -> Result<(), Hidpp20Error> {
136 self.write_value(FN_SET_BRIGHTNESS, brightness).await
137 }
138
139 pub async fn get_brightness_levels(
142 &self,
143 start_index: u8,
144 ) -> Result<LevelConfig, Hidpp20Error> {
145 self.read_levels(FN_GET_BRIGHTNESS_LEVELS, start_index)
146 .await
147 }
148
149 pub async fn set_brightness_levels(&self, levels: &SetLevels) -> Result<(), Hidpp20Error> {
151 self.write_levels(FN_SET_BRIGHTNESS_LEVELS, levels).await
152 }
153
154 pub async fn get_brightness_effective_max(&self) -> Result<u16, Hidpp20Error> {
157 self.read_value(FN_GET_BRIGHTNESS_EFFECTIVE_MAX).await
158 }
159
160 pub async fn get_color_temperature_info(&self) -> Result<ControlInfo, Hidpp20Error> {
162 self.read_info(FN_GET_COLOR_TEMPERATURE_INFO).await
163 }
164
165 pub async fn get_color_temperature(&self) -> Result<u16, Hidpp20Error> {
167 self.read_value(FN_GET_COLOR_TEMPERATURE).await
168 }
169
170 pub async fn set_color_temperature(&self, color_temperature: u16) -> Result<(), Hidpp20Error> {
175 self.write_value(FN_SET_COLOR_TEMPERATURE, color_temperature)
176 .await
177 }
178
179 pub async fn get_color_temperature_levels(
182 &self,
183 start_index: u8,
184 ) -> Result<LevelConfig, Hidpp20Error> {
185 self.read_levels(FN_GET_COLOR_TEMPERATURE_LEVELS, start_index)
186 .await
187 }
188
189 pub async fn set_color_temperature_levels(
191 &self,
192 levels: &SetLevels,
193 ) -> Result<(), Hidpp20Error> {
194 self.write_levels(FN_SET_COLOR_TEMPERATURE_LEVELS, levels)
195 .await
196 }
197
198 async fn read_info(&self, function: u8) -> Result<ControlInfo, Hidpp20Error> {
200 let payload = self.endpoint.call(function, [0; 3]).await?.extend_payload();
201 Ok(ControlInfo::from_payload(&payload))
202 }
203
204 async fn read_value(&self, function: u8) -> Result<u16, Hidpp20Error> {
206 let payload = self.endpoint.call(function, [0; 3]).await?.extend_payload();
207 Ok(be16(&payload, 0))
208 }
209
210 async fn write_value(&self, function: u8, value: u16) -> Result<(), Hidpp20Error> {
212 let [hi, lo] = value.to_be_bytes();
213 self.endpoint.call(function, [hi, lo, 0]).await?;
214 Ok(())
215 }
216
217 async fn read_levels(
219 &self,
220 function: u8,
221 start_index: u8,
222 ) -> Result<LevelConfig, Hidpp20Error> {
223 if start_index > 0x0f {
224 return Err(Hidpp20Error::Feature(ErrorType::InvalidArgument));
225 }
226 let payload = self
228 .endpoint
229 .call(function, [start_index << 4, 0, 0])
230 .await?
231 .extend_payload();
232 Ok(LevelConfig::from_payload(&payload))
233 }
234
235 async fn write_levels(&self, function: u8, levels: &SetLevels) -> Result<(), Hidpp20Error> {
237 self.endpoint
238 .call_long(function, levels.to_payload()?)
239 .await?;
240 Ok(())
241 }
242}