use crate::{Error, Result};
use winrt::{
windows::devices::bluetooth::genericattributeprofile::{
GattCharacteristic, GattClientCharacteristicConfigurationDescriptorValue,
GattCommunicationStatus, GattValueChangedEventArgs,
},
windows::foundation::{EventRegistrationToken, TypedEventHandler},
windows::storage::streams::{DataReader, DataWriter},
ComPtr, RtAsyncOperation, RtDefaultConstructible,
};
pub type NotifiyEventHandler = Box<dyn Fn(Vec<u8>) + Send>;
pub struct BLECharacteristic {
characteristic: ComPtr<GattCharacteristic>,
notify_token: Option<EventRegistrationToken>,
}
unsafe impl Send for BLECharacteristic {}
unsafe impl Sync for BLECharacteristic {}
impl BLECharacteristic {
pub fn new(characteristic: ComPtr<GattCharacteristic>) -> Self {
BLECharacteristic {
characteristic,
notify_token: None,
}
}
pub fn write_value(&self, data: &[u8]) -> Result<()> {
let writer = DataWriter::new();
writer.write_bytes(data).unwrap();
let buffer = writer.detach_buffer().unwrap().unwrap();
let result = self
.characteristic
.write_value_async(&buffer)
.unwrap()
.blocking_get()
.unwrap();
if result == GattCommunicationStatus::Success {
Ok(())
} else {
Err(Error::NotSupported("get_status".into()))
}
}
pub fn read_value(&self) -> Result<Vec<u8>> {
let result = self
.characteristic
.read_value_async()
.unwrap()
.blocking_get()
.unwrap()
.unwrap();
if result.get_status().unwrap() == GattCommunicationStatus::Success {
let value = result.get_value().unwrap().unwrap();
let reader = DataReader::from_buffer(&value).unwrap().unwrap();
let len = reader.get_unconsumed_buffer_length().unwrap() as usize;
let mut input = vec![0u8; len];
reader.read_bytes(&mut input[0..len]).unwrap();
Ok(input)
} else {
Err(Error::NotSupported("get_status".into()))
}
}
pub fn subscribe(&mut self, on_value_changed: NotifiyEventHandler) -> Result<()> {
let value_handler = TypedEventHandler::new(
move |_: *mut GattCharacteristic, args: *mut GattValueChangedEventArgs| {
let args = unsafe { &*args };
let value = args.get_characteristic_value().unwrap().unwrap();
let reader = DataReader::from_buffer(&value).unwrap().unwrap();
let len = reader.get_unconsumed_buffer_length().unwrap() as usize;
let mut input = vec![0u8; len];
reader.read_bytes(&mut input[0..len]).unwrap();
info!("changed {:?}", input);
on_value_changed(input);
Ok(())
},
);
let token = self
.characteristic
.add_value_changed(&value_handler)
.unwrap();
self.notify_token = Some(token);
let config = GattClientCharacteristicConfigurationDescriptorValue::Notify;
let status = self
.characteristic
.write_client_characteristic_configuration_descriptor_async(config)
.unwrap()
.blocking_get()
.unwrap();
info!("subscribe {:?}", status);
Ok(())
}
pub fn unsubscribe(&mut self) -> Result<()> {
if let Some(token) = self.notify_token {
self.characteristic.remove_value_changed(token).unwrap();
}
self.notify_token = None;
let config = GattClientCharacteristicConfigurationDescriptorValue::None;
let status = self
.characteristic
.write_client_characteristic_configuration_descriptor_async(config)
.unwrap()
.blocking_get()
.unwrap();
info!("unsubscribe {:?}", status);
Ok(())
}
}
impl Drop for BLECharacteristic {
fn drop(&mut self) {
if let Some(token) = self.notify_token {
let result = self.characteristic.remove_value_changed(token);
if let Err(err) = result {
info!("Drop:remove_connection_status_changed {:?}", err);
}
}
}
}