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(
134 &self,
135 cluster_index: u8,
136 cluster_effect_index: u8,
137 params: [u8; CLUSTER_EFFECT_PARAM_COUNT],
138 persistence: RgbPersistence,
139 power_mode: PowerModeTarget,
140 ) -> Result<(), Hidpp20Error> {
141 let mut args = [0; 16];
142 args[0] = cluster_index;
143 args[1] = cluster_effect_index;
144 args[2..2 + CLUSTER_EFFECT_PARAM_COUNT].copy_from_slice(¶ms);
145 args[12] = persistence.bits() | (u8::from(power_mode) << POWER_TARGET_SHIFT);
146 self.endpoint.call_long(1, args).await?;
147 Ok(())
148 }
149
150 pub async fn set_multi_led_cluster_pattern(
152 &self,
153 cluster_index: u8,
154 pattern: u8,
155 ) -> Result<(), Hidpp20Error> {
156 self.endpoint.call(2, [cluster_index, pattern, 0]).await?;
157 Ok(())
158 }
159
160 pub async fn get_nv_config(
162 &self,
163 capability: RgbNvCapabilities,
164 ) -> Result<RgbNvConfig, Hidpp20Error> {
165 let [cap_hi, cap_lo] = capability.bits().to_be_bytes();
166 let payload = self
167 .endpoint
168 .call(3, [GetOrSet::Get.into(), cap_hi, cap_lo])
169 .await?
170 .extend_payload();
171 Ok(RgbNvConfig {
172 capability: RgbNvCapabilities::from_bits_retain(be16(&payload, 1)),
173 state: payload[3],
174 param1: payload[4],
175 param2: payload[5],
176 })
177 }
178
179 pub async fn set_nv_config(
181 &self,
182 capability: RgbNvCapabilities,
183 state: u8,
184 param1: u8,
185 param2: u8,
186 ) -> Result<(), Hidpp20Error> {
187 let [cap_hi, cap_lo] = capability.bits().to_be_bytes();
188 let mut args = [0; 16];
189 args[..6].copy_from_slice(&[GetOrSet::Set.into(), cap_hi, cap_lo, state, param1, param2]);
190 self.endpoint.call_long(3, args).await?;
191 Ok(())
192 }
193
194 pub async fn get_led_bin_info(
198 &self,
199 cluster_index: u8,
200 led_bin_index: LedBinIndex,
201 backup: bool,
202 ) -> Result<[u8; LED_BIN_PARAM_COUNT], Hidpp20Error> {
203 let get_or_set = if backup {
204 GET_BACKUP
205 } else {
206 GetOrSet::Get.into()
207 };
208 let payload = self
209 .endpoint
210 .call(4, [get_or_set, cluster_index, led_bin_index.into()])
211 .await?
212 .extend_payload();
213 let mut params = [0; LED_BIN_PARAM_COUNT];
214 params.copy_from_slice(&payload[3..3 + LED_BIN_PARAM_COUNT]);
215 Ok(params)
216 }
217
218 pub async fn set_led_bin_info(
220 &self,
221 cluster_index: u8,
222 led_bin_index: LedBinIndex,
223 params: [u8; LED_BIN_PARAM_COUNT],
224 ) -> Result<(), Hidpp20Error> {
225 let mut args = [0; 16];
226 args[0] = GetOrSet::Set.into();
227 args[1] = cluster_index;
228 args[2] = led_bin_index.into();
229 args[3..3 + LED_BIN_PARAM_COUNT].copy_from_slice(¶ms);
230 self.endpoint.call_long(4, args).await?;
231 Ok(())
232 }
233
234 pub async fn get_sw_control(&self) -> Result<RgbSwControl, Hidpp20Error> {
236 let payload = self
237 .endpoint
238 .call(5, [GetOrSet::Get.into(), 0, 0])
239 .await?
240 .extend_payload();
241 Ok(RgbSwControl {
242 control: SwControlFlags::from_bits_retain(payload[1]),
243 events: EventsNotificationFlags::from_bits_retain(payload[2]),
244 })
245 }
246
247 pub async fn set_sw_control(
249 &self,
250 control: SwControlFlags,
251 events: EventsNotificationFlags,
252 ) -> Result<(), Hidpp20Error> {
253 self.endpoint
254 .call(5, [GetOrSet::Set.into(), control.bits(), events.bits()])
255 .await?;
256 Ok(())
257 }
258
259 pub async fn set_effect_sync_correction(
263 &self,
264 cluster_index: u8,
265 drift_value: i16,
266 ) -> Result<(), Hidpp20Error> {
267 let [drift_hi, drift_lo] = drift_value.to_be_bytes();
268 let mut args = [0; 16];
269 args[..4].copy_from_slice(&[cluster_index, 0, drift_hi, drift_lo]);
270 self.endpoint.call_long(6, args).await?;
271 Ok(())
272 }
273
274 pub async fn get_power_mode_config(&self) -> Result<RgbPowerModeConfig, Hidpp20Error> {
276 let payload = self
277 .endpoint
278 .call(7, [GetOrSet::Get.into(), 0, 0])
279 .await?
280 .extend_payload();
281 Ok(RgbPowerModeConfig::from_payload(&payload))
282 }
283
284 pub async fn set_power_mode_config(
286 &self,
287 config: RgbPowerModeConfig,
288 ) -> Result<(), Hidpp20Error> {
289 let [flags_hi, flags_lo] = config.flags.to_be_bytes();
290 let [psave_hi, psave_lo] = config.no_activity_timeout_to_power_save.to_be_bytes();
291 let [off_hi, off_lo] = config.no_activity_timeout_to_off.to_be_bytes();
292 let mut args = [0; 16];
293 args[..7].copy_from_slice(&[
294 GetOrSet::Set.into(),
295 flags_hi,
296 flags_lo,
297 psave_hi,
298 psave_lo,
299 off_hi,
300 off_lo,
301 ]);
302 self.endpoint.call_long(7, args).await?;
303 Ok(())
304 }
305
306 pub async fn get_power_mode(&self) -> Result<RgbPowerMode, Hidpp20Error> {
308 let payload = self
309 .endpoint
310 .call(8, [GetOrSet::Get.into(), 0, 0])
311 .await?
312 .extend_payload();
313 RgbPowerMode::try_from(payload[1]).map_err(|_| Hidpp20Error::UnsupportedResponse)
314 }
315
316 pub async fn set_power_mode(&self, mode: RgbPowerMode) -> Result<(), Hidpp20Error> {
319 self.endpoint
320 .call(8, [GetOrSet::Set.into(), mode.into(), 0])
321 .await?;
322 Ok(())
323 }
324
325 pub async fn shutdown(&self) -> Result<(), Hidpp20Error> {
329 self.endpoint.call(9, [0; 3]).await?;
330 Ok(())
331 }
332}