openlogi_hid/write/
diagnostics.rs1use std::sync::Arc;
2
3use hidpp::{
4 device::Device, feature::CreatableFeature, feature::battery_status::BatteryStatusFeature,
5 feature::feature_set::FeatureSetFeature, feature::unified_battery::UnifiedBatteryFeature,
6};
7
8use crate::reprog_controls::{self, CidFlags, CidInfo, ReprogControlsV4};
9use crate::route::DeviceRoute;
10use crate::write::{HidppOperation, WriteError, classify_hidpp_error, open_feature, with_route};
11
12#[derive(Debug, Clone, Copy)]
15pub struct FeatureEntry {
16 pub id: u16,
18 pub version: u8,
20}
21
22#[derive(Debug, Clone, Copy)]
26pub struct ReprogControlEntry {
27 pub cid: u16,
29 pub task_id: u16,
31 pub flags: CidFlags,
33}
34
35impl From<CidInfo> for ReprogControlEntry {
36 fn from(info: CidInfo) -> Self {
37 Self {
38 cid: info.cid.into(),
39 task_id: info.task_id.0,
40 flags: info.flags,
41 }
42 }
43}
44
45pub async fn dump_features(route: &DeviceRoute) -> Result<Vec<FeatureEntry>, WriteError> {
51 let index = route.device_index();
52 with_route(route, move |channel| async move {
53 let mut device = Device::new(Arc::clone(&channel), index)
54 .await
55 .map_err(|_| WriteError::DeviceUnreachable { index })?;
56 let feature_set_info = device
60 .root()
61 .get_feature(FeatureSetFeature::ID)
62 .await
63 .map_err(|e| {
64 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
65 })?
66 .ok_or(WriteError::FeatureUnsupported {
67 feature_hex: FeatureSetFeature::ID,
68 })?;
69 let feature_set = device.add_feature::<FeatureSetFeature>(feature_set_info.index);
70 let count = feature_set.count().await.map_err(|e| {
71 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
72 })?;
73 let mut entries = Vec::with_capacity(usize::from(count));
74 for i in 0..=count {
75 let info = feature_set.get_feature(i).await.map_err(|e| {
76 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
77 })?;
78 entries.push(FeatureEntry {
79 id: info.id,
80 version: info.version,
81 });
82 }
83 Ok(entries)
84 })
85 .await
86}
87
88pub async fn dump_reprog_controls(
94 route: &DeviceRoute,
95) -> Result<Vec<ReprogControlEntry>, WriteError> {
96 let index = route.device_index();
97 with_route(route, move |channel| async move {
98 let device = Device::new(Arc::clone(&channel), index)
99 .await
100 .map_err(|_| WriteError::DeviceUnreachable { index })?;
101 let info = device
102 .root()
103 .get_feature(reprog_controls::FEATURE_ID)
104 .await
105 .map_err(|e| {
106 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
107 })?
108 .ok_or(WriteError::FeatureUnsupported {
109 feature_hex: reprog_controls::FEATURE_ID,
110 })?;
111 let rc = ReprogControlsV4::new(Arc::clone(&channel), index, info.index);
112 let count = rc.get_count().await.map_err(|e| {
113 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
114 })?;
115 let mut entries = Vec::with_capacity(usize::from(count));
116 for i in 0..count {
117 let control = rc.get_cid_info(i).await.map_err(|e| {
118 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
119 })?;
120 entries.push(control.into());
121 }
122 Ok(entries)
123 })
124 .await
125}
126
127pub async fn read_battery_raw(route: &DeviceRoute) -> Result<String, WriteError> {
133 let index = route.device_index();
134 with_route(route, move |channel| async move {
135 let mut device = Device::new(Arc::clone(&channel), index)
136 .await
137 .map_err(|_| WriteError::DeviceUnreachable { index })?;
138
139 match open_feature::<UnifiedBatteryFeature>(&mut device).await {
140 Ok(feature) => {
141 let info = feature
142 .get_battery_info()
143 .await
144 .map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
145 return Ok(format!(
146 "0x1004 UnifiedBattery: percentage={} level={:?} status={:?}",
147 info.charging_percentage, info.level, info.status
148 ));
149 }
150 Err(WriteError::FeatureUnsupported { .. }) => {}
151 Err(e) => return Err(e),
152 }
153
154 match open_feature::<BatteryStatusFeature>(&mut device).await {
155 Ok(feature) => {
156 let info = feature
157 .get_battery_level_status()
158 .await
159 .map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
160 return Ok(format!(
161 "0x1000 BatteryStatus: discharge_level={} next_level={} status={:?}",
162 info.discharge_level, info.next_level, info.status
163 ));
164 }
165 Err(WriteError::FeatureUnsupported { .. }) => {}
166 Err(e) => return Err(e),
167 }
168
169 Err(WriteError::FeatureUnsupported {
172 feature_hex: 0x1004,
173 })
174 })
175 .await
176}