use std::sync::Arc;
use hidpp::{
channel::HidppChannel,
feature::{CreatableFeature, Feature},
nibble::U4,
protocol::v20::{self, Hidpp20Error},
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, IntoPrimitive, TryFromPrimitive, Serialize, Deserialize,
)]
#[repr(u8)]
pub enum SmartShiftMode {
Free = 1,
Ratchet = 2,
}
impl SmartShiftMode {
#[must_use]
pub fn flipped(self) -> Self {
match self {
Self::Free => Self::Ratchet,
Self::Ratchet => Self::Free,
}
}
}
pub const AUTO_DISENGAGE_PERMANENT: u8 = 0xff;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmartShiftStatus {
pub mode: SmartShiftMode,
pub auto_disengage: u8,
pub tunable_torque: u8,
}
#[derive(Clone)]
pub struct SmartShiftFeatureV0 {
chan: Arc<HidppChannel>,
device_index: u8,
feature_index: u8,
}
impl CreatableFeature for SmartShiftFeatureV0 {
const ID: u16 = 0x2111;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
chan,
device_index,
feature_index,
}
}
}
impl Feature for SmartShiftFeatureV0 {}
const FUNCTION_GET_STATUS: u8 = 1;
const FUNCTION_SET_STATUS: u8 = 2;
impl SmartShiftFeatureV0 {
pub async fn get_status(&self) -> Result<SmartShiftStatus, Hidpp20Error> {
let response = self
.chan
.send_v20(v20::Message::Short(
v20::MessageHeader {
device_index: self.device_index,
feature_index: self.feature_index,
function_id: U4::from_lo(FUNCTION_GET_STATUS),
software_id: self.chan.get_sw_id(),
},
[0x00, 0x00, 0x00],
))
.await?;
let payload = response.extend_payload();
let mode = SmartShiftMode::try_from(payload[0]).unwrap_or(SmartShiftMode::Ratchet);
Ok(SmartShiftStatus {
mode,
auto_disengage: payload[1],
tunable_torque: payload.get(2).copied().unwrap_or(0),
})
}
pub async fn set_status(
&self,
mode: SmartShiftMode,
auto_disengage: u8,
tunable_torque: u8,
) -> Result<(), Hidpp20Error> {
let _ = self
.chan
.send_v20(v20::Message::Short(
v20::MessageHeader {
device_index: self.device_index,
feature_index: self.feature_index,
function_id: U4::from_lo(FUNCTION_SET_STATUS),
software_id: self.chan.get_sw_id(),
},
[u8::from(mode), auto_disengage, tunable_torque],
))
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flipped_is_an_involution() {
assert_eq!(SmartShiftMode::Free.flipped(), SmartShiftMode::Ratchet);
assert_eq!(SmartShiftMode::Ratchet.flipped(), SmartShiftMode::Free);
assert_eq!(
SmartShiftMode::Free.flipped().flipped(),
SmartShiftMode::Free
);
}
}