use windows::Devices::Bluetooth::Advertisement::{
BluetoothLEAdvertisementDataSection, BluetoothLEAdvertisementPublisher,
BluetoothLEAdvertisementPublisherStatus, BluetoothLEManufacturerData,
};
use windows::Storage::Streams::{DataWriter, InMemoryRandomAccessStream};
use crate::config::DiscoveryConfig;
use crate::discovery::PeatBeacon;
use crate::error::{BleError, Result};
use crate::NodeId;
pub struct BleAdvertiser {
publisher: BluetoothLEAdvertisementPublisher,
is_advertising: bool,
node_id: Option<NodeId>,
}
impl BleAdvertiser {
pub fn new() -> Result<Self> {
let publisher = BluetoothLEAdvertisementPublisher::new()
.map_err(|e| BleError::PlatformError(format!("Failed to create publisher: {}", e)))?;
Ok(Self {
publisher,
is_advertising: false,
node_id: None,
})
}
pub fn start_advertising(&mut self, node_id: NodeId, _config: &DiscoveryConfig) -> Result<()> {
if self.is_advertising {
return Ok(());
}
self.node_id = Some(node_id);
let advertisement = self
.publisher
.Advertisement()
.map_err(|e| BleError::PlatformError(format!("Failed to get advertisement: {}", e)))?;
if let Ok(sections) = advertisement.DataSections() {
sections.Clear().ok();
}
if let Ok(manufacturer_data) = advertisement.ManufacturerData() {
manufacturer_data.Clear().ok();
}
if let Ok(data_sections) = advertisement.DataSections() {
if let Ok(section) = Self::create_service_uuid_section() {
data_sections.Append(§ion).ok();
}
}
if let Ok(manufacturer_data) = advertisement.ManufacturerData() {
if let Ok(data) = Self::create_peat_beacon_data(node_id) {
manufacturer_data.Append(&data).ok();
}
}
self.publisher
.Start()
.map_err(|e| BleError::PlatformError(format!("Failed to start publisher: {}", e)))?;
self.is_advertising = true;
log::info!("BLE advertising started for node {:08X}", node_id.as_u32());
Ok(())
}
pub fn stop_advertising(&mut self) -> Result<()> {
if !self.is_advertising {
return Ok(());
}
self.publisher
.Stop()
.map_err(|e| BleError::PlatformError(format!("Failed to stop publisher: {}", e)))?;
self.is_advertising = false;
log::info!("BLE advertising stopped");
Ok(())
}
pub fn is_advertising(&self) -> bool {
self.is_advertising
}
pub fn status(&self) -> Result<BluetoothLEAdvertisementPublisherStatus> {
self.publisher
.Status()
.map_err(|e| BleError::PlatformError(format!("Failed to get status: {}", e)))
}
fn create_service_uuid_section() -> Result<BluetoothLEAdvertisementDataSection> {
let section = BluetoothLEAdvertisementDataSection::new()
.map_err(|e| BleError::PlatformError(format!("Failed to create section: {}", e)))?;
section
.SetDataType(0x03)
.map_err(|e| BleError::PlatformError(format!("Failed to set data type: {}", e)))?;
let stream = InMemoryRandomAccessStream::new()
.map_err(|e| BleError::PlatformError(format!("Failed to create stream: {}", e)))?;
let writer = DataWriter::CreateDataWriter(&stream)
.map_err(|e| BleError::PlatformError(format!("Failed to create writer: {}", e)))?;
writer
.WriteBytes(&[0x7A, 0xF4])
.map_err(|e| BleError::PlatformError(format!("Failed to write UUID: {}", e)))?;
let buffer = writer
.DetachBuffer()
.map_err(|e| BleError::PlatformError(format!("Failed to detach buffer: {}", e)))?;
section
.SetData(&buffer)
.map_err(|e| BleError::PlatformError(format!("Failed to set data: {}", e)))?;
Ok(section)
}
fn create_peat_beacon_data(node_id: NodeId) -> Result<BluetoothLEManufacturerData> {
let data = BluetoothLEManufacturerData::new().map_err(|e| {
BleError::PlatformError(format!("Failed to create manufacturer data: {}", e))
})?;
data.SetCompanyId(0xFFFF)
.map_err(|e| BleError::PlatformError(format!("Failed to set company ID: {}", e)))?;
let beacon = PeatBeacon::new(node_id);
let beacon_bytes = beacon.encode();
let stream = InMemoryRandomAccessStream::new()
.map_err(|e| BleError::PlatformError(format!("Failed to create stream: {}", e)))?;
let writer = DataWriter::CreateDataWriter(&stream)
.map_err(|e| BleError::PlatformError(format!("Failed to create writer: {}", e)))?;
writer
.WriteBytes(&beacon_bytes)
.map_err(|e| BleError::PlatformError(format!("Failed to write beacon: {}", e)))?;
let buffer = writer
.DetachBuffer()
.map_err(|e| BleError::PlatformError(format!("Failed to detach buffer: {}", e)))?;
data.SetData(&buffer)
.map_err(|e| BleError::PlatformError(format!("Failed to set data: {}", e)))?;
Ok(data)
}
}
impl Drop for BleAdvertiser {
fn drop(&mut self) {
let _ = self.stop_advertising();
}
}