Skip to content

Commit

Permalink
Recreate client on reconnect (#16)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcus Weiner <mraerino@users.noreply.github.com>
  • Loading branch information
ansgarprause and mraerino committed Jul 5, 2023
1 parent 84cbcd0 commit bdd3fcf
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 31 deletions.
55 changes: 28 additions & 27 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::anyhow;

use bluest::{Adapter, Characteristic, Device};
use bluest::{Adapter, Characteristic, Device, DeviceId};
use futures_util::StreamExt;
use std::{future::Future, sync::Arc};
use std::sync::Arc;
use tokio::sync::mpsc;
use tracing::{debug, info, warn};

Expand All @@ -14,20 +14,25 @@ pub struct Client {
device: Device,
}

pub struct UnconnectedClient {
name: String,
pub enum UnconnectedClient {
Name(String),
DeviceId(DeviceId),
}

pub struct CharacteristicClient {
adapter: Arc<Adapter>,
_adapter: Arc<Adapter>,
responses: mpsc::Receiver<Vec<u8>>,
characteristic: Arc<Characteristic>,
device: Device,
}

impl UnconnectedClient {
pub fn new(name: String) -> Self {
Self { name }
Self::Name(name)
}

pub fn with_id(device_id: DeviceId) -> Self {
Self::DeviceId(device_id)
}

pub async fn connect(self) -> Result<Client, anyhow::Error> {
Expand All @@ -53,6 +58,13 @@ impl UnconnectedClient {
}

async fn discover_device(&self, adapter: &Adapter) -> Result<Device, anyhow::Error> {
let name = match &self {
Self::Name(name) => name,
Self::DeviceId(device_id) => {
return adapter.open_device(device_id).await.map_err(Into::into)
}
};

info!("starting scan");
let mut scan = adapter.scan(&[]).await?;
info!("scan started");
Expand All @@ -72,7 +84,7 @@ impl UnconnectedClient {
.device
.name()
.as_deref()
.map(|name| name == self.name)
.map(|n| n == name)
.unwrap_or(false)
{
return Ok(discovered_device.device);
Expand All @@ -83,22 +95,15 @@ impl UnconnectedClient {
}
}

async fn retry<F, Fut, O, E>(limit: usize, mut f: F) -> Result<O, anyhow::Error>
where
F: FnMut() -> Fut,
Fut: Future<Output = Result<O, E>>,
E: std::error::Error,
{
for _ in 0..limit {
match f().await {
Ok(r) => return Ok(r),
Err(e) => eprintln!("Retrying after error: {:?}", e),
}
impl Client {
pub async fn is_connected(&self) -> bool {
self.device.is_connected().await
}

pub fn device_id(&self) -> DeviceId {
self.device.id()
}
Err(anyhow!("Retry limit reached"))
}

impl Client {
pub async fn with_characteristic(
&self,
service_id: &str,
Expand Down Expand Up @@ -143,7 +148,7 @@ impl Client {
});

Ok(CharacteristicClient {
adapter: Arc::clone(&self.adapter),
_adapter: Arc::clone(&self.adapter),
characteristic,
device: self.device.clone(),
responses: rx,
Expand All @@ -158,11 +163,7 @@ impl CharacteristicClient {

pub async fn write_raw(&self, bytes: impl AsRef<[u8]>) -> Result<(), anyhow::Error> {
if !self.device.is_connected().await {
retry(5, || {
debug!("Connecting to device");
self.adapter.connect_device(&self.device)
})
.await?;
return Err(anyhow!("Device not connected"));
}
self.characteristic.write(bytes.as_ref()).await?;

Expand Down
2 changes: 1 addition & 1 deletion src/keyturner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct Keyturner {
}

impl Keyturner {
pub async fn new(auth_info: AuthInfo, client: Client) -> Result<Self, anyhow::Error> {
pub async fn new(auth_info: AuthInfo, client: &Client) -> Result<Self, anyhow::Error> {
let characteristic = client
.with_characteristic(KEY_TURNER_SERVICE, KEY_TURNER_USDIO)
.await?;
Expand Down
12 changes: 10 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,23 @@ async fn main() -> Result<(), anyhow::Error> {
let stream = nfc_stream::run().unwrap();
tokio::pin!(stream);

let connected_client = UnconnectedClient::new(LOCK_NAME.into()).connect().await?;
let mut connected_client = UnconnectedClient::new(LOCK_NAME.into()).connect().await?;

let auth_info = AuthInfo::read_from_file("auth-info.json").await?;

let mut keyturner = Keyturner::new(auth_info, connected_client).await?;
let mut keyturner = Keyturner::new(auth_info.clone(), &connected_client).await?;

loop {
let item = stream.next().await;

if !connected_client.is_connected().await {
info!("Reconnecting client");
connected_client = UnconnectedClient::with_id(connected_client.device_id())
.connect()
.await?;
keyturner = Keyturner::new(auth_info.clone(), &connected_client).await?;
}

info!("{:?}", item);

let Some(item) = item else {
Expand Down
2 changes: 1 addition & 1 deletion src/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct PairingClient {

base64_serde_type!(Base64Serde, STANDARD);

#[derive(Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthInfo {
pub authorization_id: u32,
#[serde(serialize_with = "serialize_key", deserialize_with = "deserialize_key")]
Expand Down

0 comments on commit bdd3fcf

Please sign in to comment.