From 3d1e2761b86a6b2af262bdbae0d0ff6befd4c260 Mon Sep 17 00:00:00 2001 From: Gregory Mallios Date: Tue, 30 Apr 2024 20:13:47 +0300 Subject: [PATCH] feat: add a3040 eq info update response --- soundcore-lib/src/devices/a3040.rs | 12 +++-- .../src/devices/a3040/eq_info_update.rs | 45 +++++++++++++++++++ .../src/devices/a3040/eq_update_command.rs | 20 +++++++++ soundcore-lib/src/models/eq_configuration.rs | 10 +++++ soundcore-lib/src/models/eq_profile.rs | 3 +- soundcore-lib/src/packets/response.rs | 5 +++ .../src/packets/response/eq_info_update.rs | 30 +++++++++++++ soundcore-lib/src/utils.rs | 19 -------- 8 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 soundcore-lib/src/devices/a3040/eq_info_update.rs create mode 100644 soundcore-lib/src/devices/a3040/eq_update_command.rs create mode 100644 soundcore-lib/src/packets/response/eq_info_update.rs diff --git a/soundcore-lib/src/devices/a3040.rs b/soundcore-lib/src/devices/a3040.rs index e7d7f9f..17c4ffb 100644 --- a/soundcore-lib/src/devices/a3040.rs +++ b/soundcore-lib/src/devices/a3040.rs @@ -1,9 +1,13 @@ +pub use bass_up_update::*; +pub use eq_info_update::*; +pub use eq_update_command::*; +pub use features::*; +pub use sound_mode_update_command::*; + mod bass_up_update; +mod eq_info_update; mod eq_update_command; mod features; mod sound_mode_update_command; -pub use bass_up_update::*; -pub use eq_update_command::*; -pub use features::*; -pub use sound_mode_update_command::*; +pub use eq_info_update::*; diff --git a/soundcore-lib/src/devices/a3040/eq_info_update.rs b/soundcore-lib/src/devices/a3040/eq_info_update.rs new file mode 100644 index 0000000..cc6fbd2 --- /dev/null +++ b/soundcore-lib/src/devices/a3040/eq_info_update.rs @@ -0,0 +1,45 @@ +use log::warn; +use nom::combinator::map; +use nom::error::context; +use nom::number::complete::le_u8; +use nom::sequence::tuple; +use strum::EnumCount; +use crate::models::EQProfile; +use crate::parsers::{ParseError, ParseResult}; + +pub fn parse_a3040_eq_info_update<'a, E: ParseError<'a>>(bytes: &'a [u8]) -> ParseResult { + context( + "parse_a3040_eq_info_update", + map(tuple((le_u8, le_u8)), |(b1, b2)| { + let eq_idx = ((b1 & 0xFF) as u16) + | (((b2 & 0xFF) as u16) << 8).clamp(0, EQProfile::COUNT as u16); + match EQProfile::from_id_le(eq_idx) { + Some(eq) => eq, + None => { + warn!("Unknown EQ profile index from eq info update: {}", eq_idx); + EQProfile::SoundcoreSignature + }, + } + }), + )(bytes) +} + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn should_parse_eq_info_update() { + let bytes = &[0x01, 0x00]; + let result = parse_a3040_eq_info_update::>(bytes); + assert_eq!(result, Ok((&[][..], EQProfile::Acoustic))); + } + + #[test] + pub fn should_default_if_index_is_out_of_bounds() { + let bytes = &[0xFF, 0x00]; + let result = parse_a3040_eq_info_update::>(bytes); + assert_eq!(result, Ok((&[][..], EQProfile::SoundcoreSignature))); + } +} \ No newline at end of file diff --git a/soundcore-lib/src/devices/a3040/eq_update_command.rs b/soundcore-lib/src/devices/a3040/eq_update_command.rs new file mode 100644 index 0000000..2fa402f --- /dev/null +++ b/soundcore-lib/src/devices/a3040/eq_update_command.rs @@ -0,0 +1,20 @@ +use crate::models::{BassUp, MonoEQConfiguration}; +use crate::packets::Packet; + +pub struct A3040EqUpdateCommand { + eq: MonoEQConfiguration, + bass_up: BassUp, +} + +impl Packet for A3040EqUpdateCommand { + fn command(&self) -> [u8; 7] { + match self.bass_up.0 { + true => [0x08, 0xEE, 0x00, 0x00, 0x00, 0x02, 0x84], + false => todo!(), + } + } + + fn payload(&self) -> Vec { + todo!() + } +} diff --git a/soundcore-lib/src/models/eq_configuration.rs b/soundcore-lib/src/models/eq_configuration.rs index 7886cba..a0bbcf1 100644 --- a/soundcore-lib/src/models/eq_configuration.rs +++ b/soundcore-lib/src/models/eq_configuration.rs @@ -10,6 +10,15 @@ pub enum EQConfiguration { Mono(MonoEQConfiguration), } +impl EQConfiguration { + pub fn set_profile(&mut self, profile: EQProfile) { + match self { + EQConfiguration::Stereo(config) => config.profile = profile, + EQConfiguration::Mono(config) => config.profile = profile, + } + } +} + #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default)] #[typeshare] pub struct StereoEQConfiguration { @@ -41,3 +50,4 @@ impl From for EQConfiguration { EQConfiguration::Mono(config) } } + diff --git a/soundcore-lib/src/models/eq_profile.rs b/soundcore-lib/src/models/eq_profile.rs index 1159e3c..8b3f844 100644 --- a/soundcore-lib/src/models/eq_profile.rs +++ b/soundcore-lib/src/models/eq_profile.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use strum::{Display, EnumIter, EnumString, FromRepr}; +use strum::{Display, EnumCount, EnumIter, EnumString, FromRepr}; use typeshare::typeshare; use super::MonoEQ; @@ -20,6 +20,7 @@ use super::MonoEQ; Serialize, Deserialize, EnumString, + EnumCount, Default, )] #[typeshare] diff --git a/soundcore-lib/src/packets/response.rs b/soundcore-lib/src/packets/response.rs index 6293038..c669e10 100644 --- a/soundcore-lib/src/packets/response.rs +++ b/soundcore-lib/src/packets/response.rs @@ -11,10 +11,12 @@ use crate::{ parsers::{parse_and_check_checksum, parse_packet_header}, }; use crate::api::SoundcoreDeviceState; +use crate::packets::response::eq_info_update::{EqInfoUpdate, parse_eq_info_update}; use crate::parsers::TaggedData; mod bass_up; mod battery; +mod eq_info_update; mod info; mod sound_mode; mod state; @@ -25,6 +27,7 @@ pub enum ResponsePacket { SoundModeUpdate(SoundModeUpdateResponse), DeviceInfo(DeviceInfoResponse), BassUpUpdate(BassUpUpdateResponse), + EqInfoUpdate(EqInfoUpdate), Unknown, } @@ -46,6 +49,7 @@ impl ResponsePacket { } ResponsePacketKind::InfoUpdate => Self::DeviceInfo(parse_device_info_packet(bytes)?.1), ResponsePacketKind::BassUpUpdate => Self::BassUpUpdate(parse_bass_up_update(bytes)?.1), + ResponsePacketKind::EqInfoUpdate => Self::EqInfoUpdate(parse_eq_info_update(bytes)?.1), _ => { // TODO: Have an array of Acks and handle those properly error!( @@ -95,6 +99,7 @@ impl StateTransformationPacket for ResponsePacket { } ResponsePacket::DeviceState(state_update) => state_update.data.transform_state(state), ResponsePacket::BassUpUpdate(packet) => packet.transform_state(state), + ResponsePacket::EqInfoUpdate(packet) => packet.transform_state(state), // No-op _ => state.clone(), } diff --git a/soundcore-lib/src/packets/response/eq_info_update.rs b/soundcore-lib/src/packets/response/eq_info_update.rs new file mode 100644 index 0000000..4eaef5a --- /dev/null +++ b/soundcore-lib/src/packets/response/eq_info_update.rs @@ -0,0 +1,30 @@ +use nom::combinator::map; +use nom::error::context; + +use crate::api::SoundcoreDeviceState; +use crate::devices::parse_a3040_eq_info_update; +use serde::{Deserialize, Serialize}; + +use crate::models::EQProfile; +use crate::packets::StateTransformationPacket; +use crate::parsers::{ParseError, ParseResult}; + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub struct EqInfoUpdate(pub EQProfile); + +pub fn parse_eq_info_update<'a, E: ParseError<'a>>( + bytes: &'a [u8], +) -> ParseResult { + context( + "parse_eq_info_update", + map(parse_a3040_eq_info_update, EqInfoUpdate), + )(bytes) +} + +impl StateTransformationPacket for EqInfoUpdate { + fn transform_state(self, state: &SoundcoreDeviceState) -> SoundcoreDeviceState { + let mut state = state.clone(); + state.eq_configuration.set_profile(self.0); + state + } +} diff --git a/soundcore-lib/src/utils.rs b/soundcore-lib/src/utils.rs index 8aaeff2..9bc7a10 100644 --- a/soundcore-lib/src/utils.rs +++ b/soundcore-lib/src/utils.rs @@ -122,22 +122,3 @@ pub(crate) fn calculate_checksum_byte(cmd: &[u8]) -> u8 { } (i & 0xFF).try_into().unwrap() } - -pub(crate) trait Clamp { - fn clamp(self, min: T, max: T) -> T; -} - -impl Clamp for T -where - T: PartialOrd + Copy, -{ - fn clamp(self, min: T, max: T) -> T { - if self > max { - max - } else if self < min { - min - } else { - self - } - } -}