use std::sync::Arc;
use hidpp::{
device::Device, feature::CreatableFeature, feature::battery_status::BatteryStatusFeature,
feature::feature_set::FeatureSetFeature, feature::unified_battery::UnifiedBatteryFeature,
};
use crate::reprog_controls::{self, CidFlags, CidInfo, ReprogControlsV4};
use crate::route::DeviceRoute;
use crate::write::{HidppOperation, WriteError, classify_hidpp_error, open_feature, with_route};
#[derive(Debug, Clone, Copy)]
pub struct FeatureEntry {
pub id: u16,
pub version: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct ReprogControlEntry {
pub cid: u16,
pub task_id: u16,
pub flags: CidFlags,
}
impl From<CidInfo> for ReprogControlEntry {
fn from(info: CidInfo) -> Self {
Self {
cid: info.cid.into(),
task_id: info.task_id.0,
flags: info.flags,
}
}
}
pub async fn dump_features(route: &DeviceRoute) -> Result<Vec<FeatureEntry>, WriteError> {
let index = route.device_index();
with_route(route, move |channel| async move {
let mut device = Device::new(Arc::clone(&channel), index)
.await
.map_err(|_| WriteError::DeviceUnreachable { index })?;
let feature_set_info = device
.root()
.get_feature(FeatureSetFeature::ID)
.await
.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
})?
.ok_or(WriteError::FeatureUnsupported {
feature_hex: FeatureSetFeature::ID,
})?;
let feature_set = device.add_feature::<FeatureSetFeature>(feature_set_info.index);
let count = feature_set.count().await.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
})?;
let mut entries = Vec::with_capacity(usize::from(count));
for i in 0..=count {
let info = feature_set.get_feature(i).await.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
})?;
entries.push(FeatureEntry {
id: info.id,
version: info.version,
});
}
Ok(entries)
})
.await
}
pub async fn dump_reprog_controls(
route: &DeviceRoute,
) -> Result<Vec<ReprogControlEntry>, WriteError> {
let index = route.device_index();
with_route(route, move |channel| async move {
let device = Device::new(Arc::clone(&channel), index)
.await
.map_err(|_| WriteError::DeviceUnreachable { index })?;
let info = device
.root()
.get_feature(reprog_controls::FEATURE_ID)
.await
.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
})?
.ok_or(WriteError::FeatureUnsupported {
feature_hex: reprog_controls::FEATURE_ID,
})?;
let rc = ReprogControlsV4::new(Arc::clone(&channel), index, info.index);
let count = rc.get_count().await.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
})?;
let mut entries = Vec::with_capacity(usize::from(count));
for i in 0..count {
let control = rc.get_cid_info(i).await.map_err(|e| {
classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
})?;
entries.push(control.into());
}
Ok(entries)
})
.await
}
pub async fn read_battery_raw(route: &DeviceRoute) -> Result<String, WriteError> {
let index = route.device_index();
with_route(route, move |channel| async move {
let mut device = Device::new(Arc::clone(&channel), index)
.await
.map_err(|_| WriteError::DeviceUnreachable { index })?;
match open_feature::<UnifiedBatteryFeature>(&mut device).await {
Ok(feature) => {
let info = feature
.get_battery_info()
.await
.map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
return Ok(format!(
"0x1004 UnifiedBattery: percentage={} level={:?} status={:?}",
info.charging_percentage, info.level, info.status
));
}
Err(WriteError::FeatureUnsupported { .. }) => {}
Err(e) => return Err(e),
}
match open_feature::<BatteryStatusFeature>(&mut device).await {
Ok(feature) => {
let info = feature
.get_battery_level_status()
.await
.map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
return Ok(format!(
"0x1000 BatteryStatus: discharge_level={} next_level={} status={:?}",
info.discharge_level, info.next_level, info.status
));
}
Err(WriteError::FeatureUnsupported { .. }) => {}
Err(e) => return Err(e),
}
Err(WriteError::FeatureUnsupported {
feature_hex: 0x1004,
})
})
.await
}