use bluer::gatt::remote::CharacteristicWriteRequest;
use bluer::gatt::WriteOp;
use futures_core::Stream;
use futures_lite::StreamExt;
use crate::{Characteristic, CharacteristicProperties, Descriptor, Result, Uuid};
#[derive(Debug, Clone)]
pub struct CharacteristicImpl {
inner: bluer::gatt::remote::Characteristic,
}
impl PartialEq for CharacteristicImpl {
fn eq(&self, other: &Self) -> bool {
self.inner.adapter_name() == other.inner.adapter_name()
&& self.inner.device_address() == other.inner.device_address()
&& self.inner.service_id() == other.inner.service_id()
&& self.inner.id() == other.inner.id()
}
}
impl Eq for CharacteristicImpl {}
impl std::hash::Hash for CharacteristicImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.adapter_name().hash(state);
self.inner.device_address().hash(state);
self.inner.service_id().hash(state);
self.inner.id().hash(state);
}
}
impl Characteristic {
pub(super) fn new(inner: bluer::gatt::remote::Characteristic) -> Characteristic {
Characteristic(CharacteristicImpl { inner })
}
}
impl CharacteristicImpl {
pub fn uuid(&self) -> Uuid {
match tokio::runtime::Handle::try_current() {
Ok(handle) => tokio::task::block_in_place(move || handle.block_on(self.uuid_async())),
Err(_) => tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
.block_on(self.uuid_async()),
}
.unwrap()
}
pub async fn uuid_async(&self) -> Result<Uuid> {
self.inner.uuid().await.map_err(Into::into)
}
pub async fn properties(&self) -> Result<CharacteristicProperties> {
self.inner.flags().await.map(Into::into).map_err(Into::into)
}
pub async fn value(&self) -> Result<Vec<u8>> {
self.inner.cached_value().await.map_err(Into::into)
}
pub async fn read(&self) -> Result<Vec<u8>> {
self.inner.read().await.map_err(Into::into)
}
pub async fn write(&self, value: &[u8]) -> Result<()> {
self.inner.write(value).await.map_err(Into::into)
}
pub async fn write_without_response(&self, value: &[u8]) -> Result<()> {
self.inner
.write_ext(
value,
&CharacteristicWriteRequest {
op_type: WriteOp::Command,
..Default::default()
},
)
.await
.map_err(Into::into)
}
pub fn max_write_len(&self) -> Result<usize> {
match tokio::runtime::Handle::try_current() {
Ok(handle) => tokio::task::block_in_place(move || handle.block_on(self.max_write_len_async())),
Err(_) => tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
.block_on(self.max_write_len_async()),
}
}
pub async fn max_write_len_async(&self) -> Result<usize> {
let mtu = self.inner.mtu().await?;
Ok(mtu - 3)
}
pub async fn notify(&self) -> Result<impl Stream<Item = Result<Vec<u8>>> + Send + Unpin + '_> {
Ok(Box::pin(self.inner.notify().await?.map(Ok)))
}
pub async fn is_notifying(&self) -> Result<bool> {
Ok(self.inner.notifying().await?.unwrap_or(false))
}
pub async fn discover_descriptors(&self) -> Result<Vec<Descriptor>> {
self.descriptors().await
}
pub async fn descriptors(&self) -> Result<Vec<Descriptor>> {
self.inner
.descriptors()
.await
.map_err(Into::into)
.map(|x| x.into_iter().map(Descriptor::new).collect())
}
}
impl From<bluer::gatt::CharacteristicFlags> for CharacteristicProperties {
fn from(flags: bluer::gatt::CharacteristicFlags) -> Self {
CharacteristicProperties {
broadcast: flags.broadcast,
read: flags.read,
write_without_response: flags.write_without_response,
write: flags.write,
notify: flags.notify,
indicate: flags.indicate,
authenticated_signed_writes: flags.authenticated_signed_writes,
extended_properties: flags.extended_properties,
reliable_write: flags.reliable_write,
writable_auxiliaries: flags.writable_auxiliaries,
}
}
}