Skip to content

Commit

Permalink
feat(soundcore_lib): ✨ Add DeviceInfo with parser for A3951 and other…
Browse files Browse the repository at this point in the history
… small fixes
  • Loading branch information
gmallios committed Oct 28, 2023
1 parent cf30f30 commit 1de7b59
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 40 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
resolver = "2"
members = [
"src-tauri/",
"soundcore-lib"
"soundcore-lib/",
"test_data/"
]

[profile.release]
Expand Down
2 changes: 2 additions & 0 deletions soundcore-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ typeshare = "1.0.1"
phf = { version = "0.11", default-features = false, features = ["macros"] }
dialoguer = { version = "0.10.4", features = ["fuzzy-select"] }

[dev-dependencies]
test_data = { path = "../test_data/" }
5 changes: 3 additions & 2 deletions soundcore-lib/examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ const BT_ADDR: &str = "AC:12:2F:6A:D2:07";
async fn main() {
let addr = BluetoothAdrr::from(BT_ADDR);
let dev = A3951::default().init(addr).await.unwrap();
let info = dev.get_status().await.unwrap();
println!("Device status: {:?}", info);
let info = dev.get_info().await.unwrap();
println!("Device info: {:?}", info);
// println!("Device status: {:?}", info);
// let charging = dev.get_battery_charging().await.unwrap();
// println!("Charging: {:?}", charging);
}
20 changes: 10 additions & 10 deletions soundcore-lib/src/compat/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::error::SoundcoreError;
use crate::models::{
Battery, DualBattery, EQConfiguration, MonoEQ, SideTone, SoundMode, StereoEQConfiguration,
Battery, EQConfiguration, MonoEQ, SideTone, SoundMode,
TouchTone, TwsStatus, WearDetection,
};
use crate::packets::ResponsePacket;
Expand Down Expand Up @@ -50,9 +50,9 @@ impl From<SoundMode> for ANCProfile {
}
}

impl Into<(BatteryLevel, BatteryCharging)> for Battery {
fn into(self) -> (BatteryLevel, BatteryCharging) {
match self {
impl From<Battery> for (BatteryLevel, BatteryCharging) {
fn from(val: Battery) -> Self {
match val {
Battery::Single(batt) => {
let left = BatteryLevel {
left: batt.level,
Expand All @@ -79,15 +79,15 @@ impl Into<(BatteryLevel, BatteryCharging)> for Battery {
}
}
// Maybe figure out how to do this better and test it
impl Into<EQWave> for MonoEQ {
fn into(self) -> EQWave {
EQWave::decode(&self.to_bytes()).unwrap()
impl From<MonoEQ> for EQWave {
fn from(val: MonoEQ) -> Self {
EQWave::decode(&val.to_bytes()).unwrap()
}
}

impl Into<(EQWave, EQWave)> for EQConfiguration {
fn into(self) -> (EQWave, EQWave) {
match self {
impl From<EQConfiguration> for (EQWave, EQWave) {
fn from(val: EQConfiguration) -> Self {
match val {
EQConfiguration::Stereo(stereo) => {
let left = stereo.eq.left.into();
let right = stereo.eq.right.into();
Expand Down
2 changes: 1 addition & 1 deletion soundcore-lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub enum SoundcoreError {
}

impl From<nom::Err<nom::error::VerboseError<&[u8]>>> for SoundcoreError {
fn from<'a>(error: nom::Err<nom::error::VerboseError<&'a [u8]>>) -> Self {
fn from(error: nom::Err<nom::error::VerboseError<&[u8]>>) -> Self {
SoundcoreError::NomParseError {
error: format!("{:?}", error),
}
Expand Down
2 changes: 2 additions & 0 deletions soundcore-lib/src/packets.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod request;
mod response;

pub use request::*;
pub use response::*;
Empty file.
26 changes: 12 additions & 14 deletions soundcore-lib/src/packets/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use nom::error::VerboseError;
pub enum ResponsePacket {
DeviceState(DeviceStateResponse),
SoundModeUpdate(SoundModeUpdateResponse),
DeviceInfo, // TODO
DeviceInfo(DeviceInfoResponse),
}

impl ResponsePacket {
Expand All @@ -24,38 +24,30 @@ impl ResponsePacket {
ResponsePacketKind::SoundModeUpdate => {
Self::SoundModeUpdate(parse_sound_mode_update_packet(bytes)?.1)
}
ResponsePacketKind::InfoUpdate => Self::DeviceInfo(parse_device_info_packet(bytes)?.1),
_ => unimplemented!(),
})
}
}

mod battery;
mod info;
mod sound_mode;
mod state;

pub use battery::*;
pub use info::*;
pub use sound_mode::*;
pub use state::*;

#[cfg(test)]
mod response_test {
use super::ResponsePacket;

const A3951_STATE_UPDATE_BYTES: [u8; 97] = [
9, 255, 0, 0, 1, 1, 1, 97, 0, 1, 1, 5, 5, 1, 0, 254, 254, 160, 150, 130, 120, 120, 120,
120, 120, 160, 150, 130, 120, 120, 120, 120, 120, 255, 255, 0, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 99, 1, 84, 1, 102, 1,
84, 0, 1, 0, 0, 0, 1, 1, 6, 0, 1, 0, 0, 0, 0, 242,
];
use test_data::a3951::*;

#[test]
fn sound_mode_update() {
let bytes = [
0x09, 0xFF, 0x00, 0x00, 0x01, 0x06, 0x01, 0x0E, 0x00, 0x00, 0x01, 0x01, 0x06, 0x26,
];

let packet = ResponsePacket::from_bytes(&bytes).unwrap();
let packet = ResponsePacket::from_bytes(&A3951_SOUND_MODE_UPDATE_BYTES).unwrap();
assert!(matches!(packet, ResponsePacket::SoundModeUpdate(_)));
}

Expand All @@ -64,4 +56,10 @@ mod response_test {
let packet = ResponsePacket::from_bytes(&A3951_STATE_UPDATE_BYTES).unwrap();
assert!(matches!(packet, ResponsePacket::DeviceState(_)));
}

#[test]
fn a3951_info_update() {
let packet = ResponsePacket::from_bytes(&A3951_INFO_UPDATE_BYTES).unwrap();
assert!(matches!(packet, ResponsePacket::DeviceInfo(_)));
}
}
1 change: 1 addition & 0 deletions soundcore-lib/src/packets/response/battery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
1 change: 1 addition & 0 deletions soundcore-lib/src/packets/response/battery/charging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
1 change: 1 addition & 0 deletions soundcore-lib/src/packets/response/battery/level.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
20 changes: 18 additions & 2 deletions soundcore-lib/src/packets/response/info.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
mod a3951;

pub use a3951::*;
use nom::{combinator::map, error::context};
use serde::{Deserialize, Serialize};

use crate::models::{DeviceFirmware, SerialNumber};
use crate::{
models::{DeviceFirmware, SerialNumber},
parsers::{SoundcoreParseError, SoundcoreParseResult},
};

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash, Default)]
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash)]
pub struct DeviceInfoResponse {
pub sn: Option<SerialNumber>,
pub fw: Option<DeviceFirmware>,
}

// TODO: Add more parsers
pub fn parse_device_info_packet<'a, E: SoundcoreParseError<'a>>(
bytes: &'a [u8],
) -> SoundcoreParseResult<DeviceInfoResponse, E> {
context("parse_device_info", |bytes| {
map(
parse_a3951_device_info_packet::<'a, E>,
DeviceInfoResponse::from,
)(bytes)
})(bytes)
}
42 changes: 42 additions & 0 deletions soundcore-lib/src/packets/response/info/a3951.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use nom::{
combinator::{all_consuming, map},
error::context,
sequence::{pair},
};
use serde::{Deserialize, Serialize};

use crate::{
models::{DeviceFirmware, SerialNumber},
parsers::{parse_dual_fw, parse_serial_number, SoundcoreParseError, SoundcoreParseResult},
};

use super::DeviceInfoResponse;

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash)]
pub struct A3951DeviceInfoResponse {
pub sn: SerialNumber,
pub fw: DeviceFirmware,
}

pub fn parse_a3951_device_info_packet<'a, E: SoundcoreParseError<'a>>(
bytes: &'a [u8],
) -> SoundcoreParseResult<A3951DeviceInfoResponse, E> {
context(
"parse_a3951_device_info",
all_consuming(map(pair(parse_dual_fw, parse_serial_number), |(fw, sn)| {
A3951DeviceInfoResponse {
fw: DeviceFirmware::DUAL(fw.0, fw.1),
sn,
}
})),
)(bytes)
}

impl From<A3951DeviceInfoResponse> for DeviceInfoResponse {
fn from(value: A3951DeviceInfoResponse) -> Self {
DeviceInfoResponse {
sn: Some(value.sn),
fw: Some(value.fw),
}
}
}
2 changes: 1 addition & 1 deletion soundcore-lib/src/packets/response/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct DeviceStateResponse {
pub touch_tone: Option<TouchTone>,
}

// TODO
// TODO: Add more parsers
pub fn parse_state_update_packet<'a, E: SoundcoreParseError<'a>>(
bytes: &'a [u8],
) -> SoundcoreParseResult<DeviceStateResponse, E> {
Expand Down
3 changes: 3 additions & 0 deletions soundcore-lib/src/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use battery::*;
pub use checksum::*;
pub use eq::*;
pub use eq_configuration::*;
pub use fw::*;
pub use game_mode::*;
pub use gender::*;
pub use hearid::*;
Expand All @@ -28,6 +29,7 @@ mod battery;
mod checksum;
mod eq;
mod eq_configuration;
mod fw;
mod game_mode;
mod gender;
mod hearid;
Expand All @@ -45,4 +47,5 @@ pub trait SoundcoreParseError<'a>: ParseError<&'a [u8]> + ContextError<&'a [u8]>
impl<'a> SoundcoreParseError<'a> for nom::error::VerboseError<&'a [u8]> {}

#[cfg(test)]
#[allow(dead_code)]
pub type TestParserError<'a> = nom::error::VerboseError<&'a [u8]>;
34 changes: 34 additions & 0 deletions soundcore-lib/src/parsers/fw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use nom::{combinator::map, error::context, sequence::pair};

use crate::models::FirmwareVer;

use super::{parse_str, SoundcoreParseError, SoundcoreParseResult};

pub fn parse_fw<'a, E: SoundcoreParseError<'a>>(
bytes: &'a [u8],
) -> SoundcoreParseResult<FirmwareVer, E> {
context(
"parse_fw",
map(parse_str(5usize), |ver| {
let (major, minor) = match ver.split_once('.') {
Some((major_str, minor_str)) => (
major_str.parse().unwrap_or_default(),
minor_str.parse().unwrap_or_default(),
),
None => (0, 0),
};
FirmwareVer::new(major, minor)
}),
)(bytes)
}

pub fn parse_dual_fw<'a, E: SoundcoreParseError<'a>>(
bytes: &'a [u8],
) -> SoundcoreParseResult<(FirmwareVer, FirmwareVer), E> {
context("map_dual_fw", pair(parse_fw, parse_fw))(bytes)
}

#[cfg(test)]
mod fw_parser {
// TODO: Add tests
}
11 changes: 2 additions & 9 deletions soundcore-lib/tests/a3951.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
use soundcore_lib::packets::ResponsePacket;

const STATE_UPDATE_BYTES: [u8; 97] = [
9, 255, 0, 0, 1, 1, 1, 97, 0, 1, 1, 5, 5, 1, 0, 254, 254, 160, 150, 130, 120, 120, 120, 120,
120, 160, 150, 130, 120, 120, 120, 120, 120, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 99, 1, 84, 1, 102, 1, 84, 0, 1, 0, 0, 0,
1, 1, 6, 0, 1, 0, 0, 0, 0, 242,
];
use test_data::a3951::A3951_STATE_UPDATE_BYTES;

#[test]
fn parse_a3951_state_update() {
let packet = ResponsePacket::from_bytes(&STATE_UPDATE_BYTES).unwrap();
let packet = ResponsePacket::from_bytes(&A3951_STATE_UPDATE_BYTES).unwrap();
match packet {
ResponsePacket::DeviceState(_state) => {
todo!()
Expand Down

0 comments on commit 1de7b59

Please sign in to comment.