br_ble/
device.rs

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