use core::iter::once;
use bluer::adv::Advertisement;
use bluer::agent::Agent;
use bluer::gatt::local::{
characteristic_control, Application, Characteristic, CharacteristicControl,
CharacteristicControlEvent, CharacteristicNotify, CharacteristicNotifyMethod,
CharacteristicWrite, CharacteristicWriteMethod, CharacteristicWriteRequest, Service,
};
use bluer::gatt::CharacteristicWriter;
use bluer::Uuid;
use embassy_futures::select::{select, select4, Either};
use tokio::sync::mpsc::Receiver;
use tokio_stream::StreamExt;
use crate::error::Error;
use crate::transport::network::btp::Btp;
use crate::transport::network::BtAddr;
use crate::utils::select::Coalesce;
use super::{AdvData, C1_CHARACTERISTIC_UUID, C2_CHARACTERISTIC_UUID, MATTER_BLE_SERVICE_UUID};
pub async fn run_peripheral(
adapter_name: Option<&str>,
service_name: &str,
service_adv_data: &AdvData,
btp: &Btp,
) -> Result<(), Error> {
let session = bluer::Session::new().await?;
let _handle = session.register_agent(Agent::default()).await?;
let adapter = if let Some(adapter_name) = adapter_name {
session.adapter(adapter_name)?
} else {
session.default_adapter().await?
};
adapter.set_powered(true).await?;
let le_advertisement = Advertisement {
discoverable: Some(true),
local_name: Some(service_name.into()),
service_uuids: once(Uuid::from_u128(MATTER_BLE_SERVICE_UUID)).collect(),
service_data: once((
Uuid::from_u128(MATTER_BLE_SERVICE_UUID),
service_adv_data.service_payload_iter().collect(),
))
.collect(),
..Default::default()
};
let (write_sender, mut write_receiver) = tokio::sync::mpsc::channel(1);
let (mut notify_cc, notify_cc_handle) = characteristic_control();
let app = Application {
services: vec![Service {
uuid: Uuid::from_u128(MATTER_BLE_SERVICE_UUID),
primary: true,
characteristics: vec![
Characteristic {
uuid: Uuid::from_u128(C1_CHARACTERISTIC_UUID),
write: Some(CharacteristicWrite {
write: true,
method: CharacteristicWriteMethod::Fun(Box::new(move |new_value, req| {
let sender = write_sender.clone();
Box::pin(async move {
sender.send((new_value, req)).await.unwrap();
Ok(())
})
})),
..Default::default()
}),
..Default::default()
},
Characteristic {
uuid: Uuid::from_u128(C2_CHARACTERISTIC_UUID),
notify: Some(CharacteristicNotify {
indicate: true,
method: CharacteristicNotifyMethod::Io,
..Default::default()
}),
control_handle: notify_cc_handle,
..Default::default()
},
],
..Default::default()
}],
..Default::default()
};
let _app_handle = adapter.serve_gatt_application(app).await?;
info!(
"Serving Matter GATT BTP service on Bluetooth adapter {}",
adapter.name()
);
loop {
let notifier = {
let _adv_handle = adapter.advertise(le_advertisement.clone()).await?;
info!(
"Advertising Matter GATT BTP service on Bluetooth adapter {}",
adapter.name(),
);
notifier(&mut notify_cc).await
};
btp.reset();
select4(
wait_complete(btp, ¬ifier),
process_write(btp, &mut write_receiver),
process_indicate(btp, None, ¬ifier, &mut [0; 512]),
process_cc_events(&mut notify_cc),
)
.coalesce()
.await?;
}
}
async fn process_write(
btp: &Btp,
receiver: &mut Receiver<(Vec<u8>, CharacteristicWriteRequest)>,
) -> Result<(), Error> {
while let Some((value, req)) = receiver.recv().await {
btp.process_incoming(Some(req.mtu), BtAddr(req.device_address.0), &value)?;
}
Ok(())
}
async fn process_indicate(
btp: &Btp,
gatt_mtu: Option<u16>,
notifier: &CharacteristicWriter,
buf: &mut [u8],
) -> Result<(), Error> {
loop {
let len = btp.process_outgoing(gatt_mtu, buf)?;
if len > 0 {
notifier.send(&buf[..len]).await?;
} else {
btp.wait_outgoing().await;
}
}
}
async fn process_cc_events(cc: &mut CharacteristicControl) -> Result<(), Error> {
loop {
let _ = notifier(cc).await;
}
}
async fn wait_complete(btp: &Btp, notifier: &CharacteristicWriter) -> Result<(), Error> {
let result = select(notifier.closed(), btp.wait_timeout()).await;
match result {
Either::First(_) => info!("Peer unsubscribed"),
Either::Second(_) => info!("Timeout while waiting for data from the peer"),
}
Ok(())
}
async fn notifier(cc: &mut CharacteristicControl) -> CharacteristicWriter {
loop {
if let Some(notifier) = cc.next().await.map(|event| {
let CharacteristicControlEvent::Notify(notifier) = event else {
unreachable!();
};
notifier
}) {
break notifier;
}
}
}