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#[derive(Clone)]
17pub struct Device {
18 pub name: String,
20 pub id: String,
22 pub rssi: i32,
24 pub characteristic: HashMap<String, Characteristic>,
26 pub discover: bool,
28 pub connect: bool,
30 pub disable: bool,
32
33 pub last_seen:Instant,
34 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 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 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 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#[derive(Clone)]
141pub struct Characteristic {
142 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 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#[derive(Debug, Clone)]
178pub struct Properties {
179 pub read: bool,
181 pub write: bool,
183 pub notify: bool,
185 pub indicate: bool,
187 pub write_without_response: bool,
189 pub authenticated_signed_writes: bool,
191 pub extended_properties: bool,
193 pub reliable_write: bool,
195 pub writable_auxiliaries: bool,
197 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}