br_ble/
device.rs

1use std::collections::HashMap;
2use json::{JsonValue, object};
3#[cfg(target_os = "windows")]
4use windows::Devices::Bluetooth::BluetoothLEDevice;
5#[cfg(target_os = "windows")]
6use windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic;
7#[cfg(target_os = "windows")]
8use windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties;
9#[cfg(target_os = "macos")]
10use crate::mac::central::{AdvertisementData, CentralManager};
11#[cfg(target_os = "macos")]
12use crate::mac::central::peripheral::Peripheral;
13
14/// 蓝牙设备
15#[derive(Clone)]
16pub struct Device {
17    /// 设备名称
18    pub name: String,
19    /// 设备id
20    pub uuid: String,
21    /// 设备信号
22    pub rssi: i32,
23    /// 服务列表
24    pub characteristic: HashMap<String, Characteristic>,
25    /// 发现
26    pub discover: bool,
27    /// 连接
28    pub connect: bool,
29    /// 禁用
30    pub disable: bool,
31    /// 环境类型
32    pub types: Types,
33}
34impl Device {
35    #[cfg(target_os = "macos")]
36    pub fn new(uuid: String, name: String, rssi: i32, manager: CentralManager, peripheral: Peripheral, advertisement_data: AdvertisementData) -> Self {
37        Self {
38            name,
39            uuid,
40            rssi,
41            characteristic: Default::default(),
42            discover: false,
43            connect: false,
44            disable: false,
45            types: Types::Mac(manager, peripheral, Box::new(advertisement_data)),
46        }
47    }
48    #[cfg(target_os = "windows")]
49    pub fn new(uuid: String, name: String, rssi: i32, manager: CentralManager, peripheral: BluetoothLEDevice, advertisement_data: AdvertisementData) -> Self {
50        Self {
51            name,
52            uuid,
53            rssi,
54            characteristic: Default::default(),
55            discover: false,
56            connect: false,
57            disable: false,
58            types: Types::Win(manager, peripheral, advertisement_data),
59        }
60    }
61    /// 关闭连接
62    pub fn disconnect(&mut self) -> bool {
63        match self.clone().types {
64            #[cfg(target_os = "macos")]
65            Types::Mac(manager, peripheral, _advertisement_data) => {
66                manager.cancel_connect(&peripheral);
67                true
68            }
69            #[cfg(target_os = "windows")]
70            Types::Win(manager, peripheral, _2) => {
71                manager.disconnect(peripheral);
72                true
73            }
74            Types::None => { false }
75        }
76    }
77    /// 开启连接
78    pub fn connect(&mut self) -> bool {
79        match self.clone().types {
80            #[cfg(target_os = "macos")]
81            Types::Mac(manager, peripheral, _advertisement_data) => {
82                manager.connect(&peripheral);
83                true
84            }
85            #[cfg(target_os = "windows")]
86            Types::Win(manager, BluetoothLEDevice, _2) => {
87                manager.connect(BluetoothLEDevice);
88                true
89            }
90            Types::None => false
91        }
92    }
93    /// 订阅
94    pub fn subscribe(&mut self, characteristic: Characteristic) -> bool {
95        match self.clone().types {
96            #[cfg(target_os = "macos")]
97            Types::Mac(_, peripheral, _) => {
98                peripheral.subscribe(&characteristic.characteristic);
99                true
100            }
101            #[cfg(target_os = "windows")]
102            Types::Win(manager, BluetoothLEDevice, _2) => {
103                manager.subscribe(BluetoothLEDevice, characteristic);
104                true
105            }
106            _ => false
107        }
108    }
109    pub fn json(&mut self) -> JsonValue {
110        object! {
111            name:self.name.clone(),
112            uuid:self.uuid.clone(),
113            rssi:self.rssi,
114            discover: self.discover,
115            connect: self.connect,
116            disable:self.disable,
117        }
118    }
119}
120#[derive(Clone)]
121pub enum Types {
122    #[cfg(target_os = "macos")]
123    Mac(CentralManager, Peripheral, Box<AdvertisementData>),
124    #[cfg(target_os = "windows")]
125    Win(CentralManager, BluetoothLEDevice, AdvertisementData),
126    None,
127}
128
129#[cfg(target_os = "macos")]
130use crate::mac::central::characteristic::Characteristic as Char;
131#[cfg(target_os = "windows")]
132use crate::win::central::{AdvertisementData, CentralManager};
133
134/// 特征
135#[derive(Clone)]
136pub struct Characteristic {
137    /// 特征UUID
138    pub uuid: String,
139    pub properties: Properties,
140    #[cfg(target_os = "macos")]
141    characteristic: Char,
142    #[cfg(target_os = "windows")]
143    pub characteristic: GattCharacteristicProperties,
144    #[cfg(target_os = "windows")]
145    pub gatt_characteristic: GattCharacteristic,
146    /// 是否订阅
147    pub is_subscribe: Option<bool>,
148}
149
150impl Characteristic {
151    #[cfg(target_os = "macos")]
152    pub fn default(characteristic: Char) -> Characteristic {
153        Self {
154            uuid: characteristic.id().to_string(),
155            properties: Properties::form(format!("{:?}", characteristic.properties())),
156            characteristic,
157            is_subscribe: None,
158        }
159    }
160    #[cfg(target_os = "windows")]
161    pub fn default(uuid: String, characteristic: GattCharacteristicProperties, gatt_characteristic: GattCharacteristic) -> Characteristic {
162        Self {
163            uuid,
164            properties: Properties::form(characteristic.0),
165            characteristic,
166            gatt_characteristic,
167            is_subscribe: None,
168        }
169    }
170}
171/// 属性标志
172#[derive(Debug, Clone)]
173pub struct Properties {
174    /// 可读
175    pub read: bool,
176    /// 可写
177    pub write: bool,
178    /// 通知
179    pub notify: bool,
180    /// 发送指示
181    pub indicate: bool,
182    /// 无响应写入
183    pub write_without_response: bool,
184    /// 认证签名写入
185    pub authenticated_signed_writes: bool,
186    /// 指示特征具有扩展的属性
187    pub extended_properties: bool,
188    /// 支持可靠写入
189    pub reliable_write: bool,
190    /// 支持可写辅助属性
191    pub writable_auxiliaries: bool,
192    /// 允许特征值进行广播
193    pub broadcast: bool,
194}
195
196impl Properties {
197    const BROADCAST: u16 = 0b00000001;
198    const READ: u16 = 0b00000010;
199    const WRITE_WITHOUT_RESPONSE: u16 = 0b00000100;
200    const WRITE: u16 = 0b00001000;
201    const NOTIFY: u16 = 0b00010000;
202    const INDICATE: u16 = 0b00100000;
203    const AUTHENTICATED_SIGNED_WRITES: u16 = 0b01000000;
204    const EXTENDED_PROPERTIES: u16 = 0b10000000;
205    const RELIABLE_WRITE: u16 = 0b000100000000;
206    const WRITABLE_AUXILIARIES: u16 = 0b001000000000;
207
208    #[cfg(target_os = "macos")]
209    pub fn form(text: String) -> Properties {
210        let text = text.trim_start_matches("Properties(BitFlagsDebug(BitFlags<Property>(");
211        let text = text.split(",").collect::<Vec<&str>>()[0];
212        let text = text.trim_start_matches("0b");
213        let raw_value = u16::from_str_radix(text, 2).map_err(|e| e.to_string()).unwrap();
214        let is_read = (Properties::READ & raw_value) == Properties::READ;
215        let is_write = (Properties::WRITE & raw_value) == Properties::WRITE;
216        let is_notify = (Properties::NOTIFY & raw_value) == Properties::NOTIFY;
217        let is_indicate = (Properties::INDICATE & raw_value) == Properties::INDICATE;
218        let is_write_without_response = (Properties::WRITE_WITHOUT_RESPONSE & raw_value) == Properties::WRITE_WITHOUT_RESPONSE;
219        let is_authenticated_signed_writes = (Properties::AUTHENTICATED_SIGNED_WRITES & raw_value) == Properties::AUTHENTICATED_SIGNED_WRITES;
220        let is_extended_properties = (Properties::EXTENDED_PROPERTIES & raw_value) == Properties::EXTENDED_PROPERTIES;
221        let is_reliable_write = (Properties::RELIABLE_WRITE & raw_value) == Properties::RELIABLE_WRITE;
222        let is_writable_auxiliaries = (Properties::WRITABLE_AUXILIARIES & raw_value) == Properties::WRITABLE_AUXILIARIES;
223        let is_broadcast = (Properties::BROADCAST & raw_value) == Properties::BROADCAST;
224        Self {
225            read: is_read,
226            write: is_write,
227            notify: is_notify,
228            indicate: is_indicate,
229            write_without_response: is_write_without_response,
230            authenticated_signed_writes: is_authenticated_signed_writes,
231            extended_properties: is_extended_properties,
232            reliable_write: is_reliable_write,
233            writable_auxiliaries: is_writable_auxiliaries,
234            broadcast: is_broadcast,
235        }
236    }
237    #[cfg(target_os = "windows")]
238    pub fn form(properties: u32) -> Properties {
239        let is_read = if (properties & GattCharacteristicProperties::Read.0) != 0 { true } else { false };
240        let is_write = if (properties & GattCharacteristicProperties::Write.0) != 0 { true } else { false };
241        let is_notify = if (properties & GattCharacteristicProperties::Notify.0) != 0 { true } else { false };
242        let is_indicate = if (properties & GattCharacteristicProperties::Indicate.0) != 0 { true } else { false };
243        let is_write_without_response = if (properties & GattCharacteristicProperties::WriteWithoutResponse.0) != 0 { true } else { false };
244        let is_authenticated_signed_writes = if (properties & GattCharacteristicProperties::AuthenticatedSignedWrites.0) != 0 { true } else { false };
245        let is_extended_properties = if (properties & GattCharacteristicProperties::ExtendedProperties.0) != 0 { true } else { false };
246        let is_reliable_write = if (properties & GattCharacteristicProperties::ReliableWrites.0) != 0 { true } else { false };
247        let is_writable_auxiliaries = if (properties & GattCharacteristicProperties::WritableAuxiliaries.0) != 0 { true } else { false };
248        let is_broadcast = if (properties & GattCharacteristicProperties::Broadcast.0) != 0 { true } else { false };
249        Self {
250            read: is_read,
251            write: is_write,
252            notify: is_notify,
253            indicate: is_indicate,
254            write_without_response: is_write_without_response,
255            authenticated_signed_writes: is_authenticated_signed_writes,
256            extended_properties: is_extended_properties,
257            reliable_write: is_reliable_write,
258            writable_auxiliaries: is_writable_auxiliaries,
259            broadcast: is_broadcast,
260        }
261    }
262}