Skip to content

Add reconnect functionality and improve error handling for Ear2 device #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions nothing/src/connect.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
use bluer::rfcomm::Stream;
use bluer::Address;

async fn set_powered_adapter(
session: bluer::Session,
) -> Result<bluer::Adapter, Box<dyn std::error::Error>> {
use bluer::Error;
use bluer::{Address, ErrorKind};
async fn set_powered_adapter(session: bluer::Session) -> Result<bluer::Adapter, Error> {
let adapter = session.default_adapter().await?;
adapter.set_powered(true).await?;
Ok(adapter)
}

async fn find_address(
adapter: bluer::Adapter,
address: [u8; 3],
) -> Result<Address, Box<dyn std::error::Error>> {
async fn find_address(adapter: bluer::Adapter, address: [u8; 3]) -> Result<Address, Error> {
let device_addresses = adapter.device_addresses().await?;

let ear_address = device_addresses
.iter()
.find(|&addr| match addr.0 {
[a, b, c, _, _, _] => a == address[0] && b == address[1] && c == address[2],
})
.ok_or_else(|| {
"Couldn't find any Ear devices connected. Make sure you're paired with your Ear."
.ok_or(Error {
kind: ErrorKind::ConnectionAttemptFailed,
message:
"Couldn't find any Ear devices connected. Make sure you're paired with your Ear."
.to_string(),
})?;

Ok(*ear_address)
}

pub async fn connect(address: [u8; 3], channel: u8) -> Result<Stream, Box<dyn std::error::Error>> {
pub async fn connect(address: [u8; 3], channel: u8) -> Result<Stream, Error> {
let session = bluer::Session::new().await?;
let adapter = set_powered_adapter(session).await?;
let ear_address = find_address(adapter, address).await?;
Expand Down
15 changes: 9 additions & 6 deletions nothing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ pub mod connect;
pub mod nothing_ear_2;

pub trait Nothing {
fn get_address(&self) -> impl std::future::Future<Output = String> + Send;
fn get_firmware_version(&self) -> impl std::future::Future<Output = String> + Send;
fn get_serial_number(&self) -> impl std::future::Future<Output = String> + Send;
fn get_address(&self) -> impl std::future::Future<Output = Option<String>> + Send;
fn get_firmware_version(&self) -> impl std::future::Future<Output = Option<String>> + Send;
fn get_serial_number(&self) -> impl std::future::Future<Output = Option<String>> + Send;

fn set_anc_mode(
&self,
&mut self,
mode: AncMode,
) -> impl std::future::Future<Output = Result<(), bluer::Error>> + Send;

fn set_low_latency_mode(
&self,
&mut self,
mode: bool,
) -> impl std::future::Future<Output = Result<(), bluer::Error>> + Send;

fn set_in_ear_detection_mode(
&self,
&mut self,
mode: bool,
) -> impl std::future::Future<Output = Result<(), bluer::Error>> + Send;

fn try_connect(&mut self)
-> impl std::future::Future<Output = Result<(), bluer::Error>> + Send;
}
153 changes: 95 additions & 58 deletions nothing/src/nothing_ear_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::anc::AncMode;
use crate::connect::connect;
use crate::Nothing;
use bluer::rfcomm::Stream;
use bluer::Error;
use bluer::{Error, ErrorKind};
use std::future::Future;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::Mutex;
Expand Down Expand Up @@ -34,71 +34,95 @@ const EAR2_IN_EAR_DETECTION_OFF: [u8; 13] = [
0x55, 0x60, 0x01, 0x04, 0xf0, 0x03, 0x00, 0x25, 0x01, 0x01, 0x00, 0xb2, 0x94,
];

pub type Ear2State = Mutex<Ear2>;

pub struct Ear2 {
pub address: String,
pub firmware_version: String,
pub serial_number: String,
stream: Mutex<Stream>,
pub address: Option<String>,
pub firmware_version: Option<String>,
pub serial_number: Option<String>,
stream: Option<Stream>,
}

impl Nothing for Ear2 {
async fn get_address(&self) -> String {
async fn get_address(&self) -> Option<String> {
self.address.clone()
}
async fn get_firmware_version(&self) -> String {
async fn get_firmware_version(&self) -> Option<String> {
self.firmware_version.clone()
}
async fn get_serial_number(&self) -> String {
async fn get_serial_number(&self) -> Option<String> {
self.serial_number.clone()
}

fn set_anc_mode(&self, mode: AncMode) -> impl Future<Output = Result<(), bluer::Error>> + Send {
fn set_anc_mode(&mut self, mode: AncMode) -> impl Future<Output = Result<(), Error>> + Send {
self.set_anc(mode)
}

fn set_low_latency_mode(&self, mode: bool) -> impl Future<Output = Result<(), Error>> + Send {
fn set_low_latency_mode(
&mut self,
mode: bool,
) -> impl Future<Output = Result<(), Error>> + Send {
self.set_low_latency(mode)
}

fn set_in_ear_detection_mode(
&self,
&mut self,
mode: bool,
) -> impl Future<Output = Result<(), Error>> + Send {
self.set_in_ear_detection(mode)
}

fn try_connect(&mut self) -> impl Future<Output = Result<(), Error>> + Send {
self.try_connect()
}
}

impl Ear2 {
pub async fn new() -> bluer::Result<Self> {
let mut stream = Self::fetch_stream().await;

// get firmware version
stream.write_all(&EAR2_FIRMWARE).await?;
let mut buf = [0_u8; 8];
stream.read_exact(&mut buf).await?;
let version_str_len: usize = buf[5].try_into().unwrap();
let mut buf = vec![0_u8; version_str_len + 2];
stream.read_exact(&mut buf).await?;
let version = String::from_utf8_lossy(&buf[..version_str_len]);

// get serial number
stream.write_all(&EAR2_SERIAL).await?;
let mut buf = [0_u8; 64 + 64 + 18];
stream.read_exact(&mut buf).await?;
let serial = String::from_utf8_lossy(&buf[37..53]);

Ok(Self {
address: stream.peer_addr()?.addr.to_string(),
firmware_version: version.to_string(),
serial_number: serial.to_string(),
stream: Mutex::new(stream),
})
let mut ear_2 = Self {
address: None,
firmware_version: None,
serial_number: None,
stream: None,
};

// try to connect to the device
ear_2.try_connect().await?;

Ok(ear_2)
}

pub async fn try_connect(&mut self) -> bluer::Result<()> {
let stream = Self::fetch_stream().await;
if let Some(mut stream) = stream {
// get firmware version
stream.write_all(&EAR2_FIRMWARE).await?;
let mut buf = [0_u8; 8];
stream.read_exact(&mut buf).await?;
let version_str_len: usize = buf[5].try_into().unwrap();
let mut buf = vec![0_u8; version_str_len + 2];
stream.read_exact(&mut buf).await?;
let version = String::from_utf8_lossy(&buf[..version_str_len]);

// get serial number
stream.write_all(&EAR2_SERIAL).await?;
let mut buf = [0_u8; 64 + 64 + 18];
stream.read_exact(&mut buf).await?;
let serial = String::from_utf8_lossy(&buf[37..53]);

self.address = Some(stream.peer_addr()?.addr.to_string());
self.firmware_version = Some(version.to_string());
self.serial_number = Some(serial.to_string());
self.stream = Some(stream);
}

Ok(())
}

pub async fn fetch_stream() -> Stream {
pub async fn fetch_stream() -> Option<Stream> {
let mut stream = connect(EAR2_ADDRESS, EAR2_CHANNEL).await;
for i in 1..=RETRY {
sleep(std::time::Duration::from_millis(i * 500)).await;
sleep(std::time::Duration::from_millis(i * 690)).await;
match stream {
Ok(s) => {
stream = Ok(s);
Expand All @@ -111,40 +135,53 @@ impl Ear2 {
}
}

stream.expect("Failed to connect to Ear")
stream.ok()
}

pub async fn set_anc(&self, mode: AncMode) -> bluer::Result<()> {
pub async fn set_anc(&mut self, mode: AncMode) -> bluer::Result<()> {
let mut buf = EAR2_ANC;
buf[9] = mode.into();

let mut stream = self.stream.lock().await;
stream.write_all(&buf).await?;

Ok(())
if let Some(stream) = &mut self.stream {
stream.write_all(&buf).await?;
Ok(())
} else {
Err(Error {
kind: ErrorKind::ConnectionAttemptFailed,
message: "set_acn failed because of no connection".to_string(),
})
}
}

pub async fn set_low_latency(&self, mode: bool) -> bluer::Result<()> {
let mut stream = self.stream.lock().await;

if mode {
stream.write_all(&EAR2_LOW_LAG_ON).await?;
pub async fn set_low_latency(&mut self, mode: bool) -> bluer::Result<()> {
if let Some(stream) = &mut self.stream {
if mode {
stream.write_all(&EAR2_LOW_LAG_ON).await?;
} else {
stream.write_all(&EAR2_LOW_LAG_OFF).await?;
}
Ok(())
} else {
stream.write_all(&EAR2_LOW_LAG_OFF).await?;
Err(Error {
kind: ErrorKind::ConnectionAttemptFailed,
message: "set_low_latency failed because of no connection".to_string(),
})
}

Ok(())
}

pub async fn set_in_ear_detection(&self, mode: bool) -> bluer::Result<()> {
let mut stream = self.stream.lock().await;

if mode {
stream.write_all(&EAR2_IN_EAR_DETECTION_ON).await?;
pub async fn set_in_ear_detection(&mut self, mode: bool) -> bluer::Result<()> {
if let Some(stream) = &mut self.stream {
if mode {
stream.write_all(&EAR2_IN_EAR_DETECTION_ON).await?;
} else {
stream.write_all(&EAR2_IN_EAR_DETECTION_OFF).await?;
}
Ok(())
} else {
stream.write_all(&EAR2_IN_EAR_DETECTION_OFF).await?;
Err(Error {
kind: ErrorKind::ConnectionAttemptFailed,
message: "set_in_ear_detection failed because of no connection".to_string(),
})
}

Ok(())
}
}
Loading