1pub mod event;
19pub mod types;
20
21#[cfg(test)]
22mod tests;
23
24use openlogi_hidpp_derive::Feature;
25
26pub use event::RgbEffectsEvent;
27pub use types::{
28 ActivityEventType, CLUSTER_EFFECT_PARAM_COUNT, DisplayPersistencyCapabilities,
29 EventsNotificationFlags, LED_BIN_PARAM_COUNT, LedBinIndex, ONBOARD_INFO_PARAM_COUNT,
30 PowerModeTarget, RgbClusterInfo, RgbDeviceInfo, RgbEffectInfo, RgbExtCapabilities,
31 RgbNvCapabilities, RgbNvConfig, RgbPersistence, RgbPowerMode, RgbPowerModeConfig, RgbSwControl,
32 SlotInfoType, SwControlFlags,
33};
34
35use self::types::{ALL_CLUSTERS, ALL_EFFECTS, GetOrSet, be16};
36use crate::{
37 feature::{EventSource, FeatureEndpoint},
38 protocol::v20::Hidpp20Error,
39};
40
41const TYPE_GENERAL_INFO: u8 = 0x00;
43const TYPE_ONBOARD_EFFECT: u8 = 0x01;
45const GET_BACKUP: u8 = 0x02;
47const POWER_TARGET_SHIFT: u8 = 2;
49
50#[derive(Feature)]
52#[creatable(id = 0x8071, version = 0)]
53pub struct RgbEffectsFeature {
54 endpoint: FeatureEndpoint,
56
57 events: EventSource<RgbEffectsEvent>,
59}
60
61impl RgbEffectsFeature {
62 pub async fn get_device_info(&self) -> Result<RgbDeviceInfo, Hidpp20Error> {
64 let payload = self
65 .endpoint
66 .call(0, [ALL_CLUSTERS, ALL_EFFECTS, TYPE_GENERAL_INFO])
67 .await?
68 .extend_payload();
69 Ok(RgbDeviceInfo::from_payload(&payload))
70 }
71
72 pub async fn get_cluster_info(
75 &self,
76 cluster_index: u8,
77 ) -> Result<RgbClusterInfo, Hidpp20Error> {
78 let payload = self
79 .endpoint
80 .call(0, [cluster_index, ALL_EFFECTS, TYPE_GENERAL_INFO])
81 .await?
82 .extend_payload();
83 Ok(RgbClusterInfo::from_payload(&payload))
84 }
85
86 pub async fn get_effect_info(
89 &self,
90 cluster_index: u8,
91 cluster_effect_index: u8,
92 ) -> Result<RgbEffectInfo, Hidpp20Error> {
93 let payload = self
94 .endpoint
95 .call(0, [cluster_index, cluster_effect_index, TYPE_GENERAL_INFO])
96 .await?
97 .extend_payload();
98 Ok(RgbEffectInfo::from_payload(&payload))
99 }
100
101 pub async fn get_onboard_effect_info(
107 &self,
108 cluster_index: u8,
109 cluster_effect_index: u8,
110 slot: u8,
111 slot_info_type: SlotInfoType,
112 ) -> Result<[u8; ONBOARD_INFO_PARAM_COUNT], Hidpp20Error> {
113 let mut args = [0; 16];
114 args[..5].copy_from_slice(&[
115 cluster_index,
116 cluster_effect_index,
117 TYPE_ONBOARD_EFFECT,
118 slot,
119 slot_info_type.into(),
120 ]);
121 let payload = self.endpoint.call_long(0, args).await?.extend_payload();
122 let mut params = [0; ONBOARD_INFO_PARAM_COUNT];
123 params.copy_from_slice(&payload[3..3 + ONBOARD_INFO_PARAM_COUNT]);
124 Ok(params)
125 }
126
127 pub async fn set_rgb_cluster_effect(
138 &self,
139 cluster_index: u8,
140 cluster_effect_index: u8,
141 params: [u8; CLUSTER_EFFECT_PARAM_COUNT],
142 persistence: RgbPersistence,
143 power_mode: PowerModeTarget,
144 ) -> Result<(), Hidpp20Error> {
145 let mut args = [0; 16];
146 args[0] = cluster_index;
147 args[1] = cluster_effect_index;
148 args[2..2 + CLUSTER_EFFECT_PARAM_COUNT].copy_from_slice(¶ms);
149 args[12] = persistence.bits() | (u8::from(power_mode) << POWER_TARGET_SHIFT);
150 self.endpoint.call_long_write_through(1, args).await?;
151 Ok(())
152 }
153
154 pub async fn set_multi_led_cluster_pattern(
156 &self,
157 cluster_index: u8,
158 pattern: u8,
159 ) -> Result<(), Hidpp20Error> {
160 self.endpoint.call(2, [cluster_index, pattern, 0]).await?;
161 Ok(())
162 }
163
164 pub async fn get_nv_config(
166 &self,
167 capability: RgbNvCapabilities,
168 ) -> Result<RgbNvConfig, Hidpp20Error> {
169 let [cap_hi, cap_lo] = capability.bits().to_be_bytes();
170 let payload = self
171 .endpoint
172 .call(3, [GetOrSet::Get.into(), cap_hi, cap_lo])
173 .await?
174 .extend_payload();
175 Ok(RgbNvConfig {
176 capability: RgbNvCapabilities::from_bits_retain(be16(&payload, 1)),
177 state: payload[3],
178 param1: payload[4],
179 param2: payload[5],
180 })
181 }
182
183 pub async fn set_nv_config(
185 &self,
186 capability: RgbNvCapabilities,
187 state: u8,
188 param1: u8,
189 param2: u8,
190 ) -> Result<(), Hidpp20Error> {
191 let [cap_hi, cap_lo] = capability.bits().to_be_bytes();
192 let mut args = [0; 16];
193 args[..6].copy_from_slice(&[GetOrSet::Set.into(), cap_hi, cap_lo, state, param1, param2]);
194 self.endpoint.call_long(3, args).await?;
195 Ok(())
196 }
197
198 pub async fn get_led_bin_info(
202 &self,
203 cluster_index: u8,
204 led_bin_index: LedBinIndex,
205 backup: bool,
206 ) -> Result<[u8; LED_BIN_PARAM_COUNT], Hidpp20Error> {
207 let get_or_set = if backup {
208 GET_BACKUP
209 } else {
210 GetOrSet::Get.into()
211 };
212 let payload = self
213 .endpoint
214 .call(4, [get_or_set, cluster_index, led_bin_index.into()])
215 .await?
216 .extend_payload();
217 let mut params = [0; LED_BIN_PARAM_COUNT];
218 params.copy_from_slice(&payload[3..3 + LED_BIN_PARAM_COUNT]);
219 Ok(params)
220 }
221
222 pub async fn set_led_bin_info(
224 &self,
225 cluster_index: u8,
226 led_bin_index: LedBinIndex,
227 params: [u8; LED_BIN_PARAM_COUNT],
228 ) -> Result<(), Hidpp20Error> {
229 let mut args = [0; 16];
230 args[0] = GetOrSet::Set.into();
231 args[1] = cluster_index;
232 args[2] = led_bin_index.into();
233 args[3..3 + LED_BIN_PARAM_COUNT].copy_from_slice(¶ms);
234 self.endpoint.call_long(4, args).await?;
235 Ok(())
236 }
237
238 pub async fn get_sw_control(&self) -> Result<RgbSwControl, Hidpp20Error> {
240 let payload = self
241 .endpoint
242 .call(5, [GetOrSet::Get.into(), 0, 0])
243 .await?
244 .extend_payload();
245 Ok(RgbSwControl {
246 control: SwControlFlags::from_bits_retain(payload[1]),
247 events: EventsNotificationFlags::from_bits_retain(payload[2]),
248 })
249 }
250
251 pub async fn set_sw_control(
258 &self,
259 control: SwControlFlags,
260 events: EventsNotificationFlags,
261 ) -> Result<(), Hidpp20Error> {
262 self.endpoint
263 .call_echoed_write_through(5, [GetOrSet::Set.into(), control.bits(), events.bits()])
264 .await?;
265 Ok(())
266 }
267
268 pub async fn set_effect_sync_correction(
272 &self,
273 cluster_index: u8,
274 drift_value: i16,
275 ) -> Result<(), Hidpp20Error> {
276 let [drift_hi, drift_lo] = drift_value.to_be_bytes();
277 let mut args = [0; 16];
278 args[..4].copy_from_slice(&[cluster_index, 0, drift_hi, drift_lo]);
279 self.endpoint.call_long(6, args).await?;
280 Ok(())
281 }
282
283 pub async fn get_power_mode_config(&self) -> Result<RgbPowerModeConfig, Hidpp20Error> {
285 let payload = self
286 .endpoint
287 .call(7, [GetOrSet::Get.into(), 0, 0])
288 .await?
289 .extend_payload();
290 Ok(RgbPowerModeConfig::from_payload(&payload))
291 }
292
293 pub async fn set_power_mode_config(
295 &self,
296 config: RgbPowerModeConfig,
297 ) -> Result<(), Hidpp20Error> {
298 let [flags_hi, flags_lo] = config.flags.to_be_bytes();
299 let [psave_hi, psave_lo] = config.no_activity_timeout_to_power_save.to_be_bytes();
300 let [off_hi, off_lo] = config.no_activity_timeout_to_off.to_be_bytes();
301 let mut args = [0; 16];
302 args[..7].copy_from_slice(&[
303 GetOrSet::Set.into(),
304 flags_hi,
305 flags_lo,
306 psave_hi,
307 psave_lo,
308 off_hi,
309 off_lo,
310 ]);
311 self.endpoint.call_long(7, args).await?;
312 Ok(())
313 }
314
315 pub async fn get_power_mode(&self) -> Result<RgbPowerMode, Hidpp20Error> {
317 let payload = self
318 .endpoint
319 .call(8, [GetOrSet::Get.into(), 0, 0])
320 .await?
321 .extend_payload();
322 RgbPowerMode::try_from(payload[1]).map_err(|_| Hidpp20Error::UnsupportedResponse)
323 }
324
325 pub async fn set_power_mode(&self, mode: RgbPowerMode) -> Result<(), Hidpp20Error> {
328 self.endpoint
329 .call(8, [GetOrSet::Set.into(), mode.into(), 0])
330 .await?;
331 Ok(())
332 }
333
334 pub async fn shutdown(&self) -> Result<(), Hidpp20Error> {
338 self.endpoint.call(9, [0; 3]).await?;
339 Ok(())
340 }
341}