1use bluez_generated::{
2 OrgBluezAdapter1Properties, OrgBluezDevice1Properties, OrgBluezGattCharacteristic1Properties,
3 ORG_BLUEZ_ADAPTER1_NAME, ORG_BLUEZ_DEVICE1_NAME, ORG_BLUEZ_GATT_CHARACTERISTIC1_NAME,
4};
5use dbus::message::{MatchRule, SignalArgs};
6use dbus::nonblock::stdintf::org_freedesktop_dbus::{
7 ObjectManagerInterfacesAdded, PropertiesPropertiesChanged,
8};
9use dbus::{Message, Path};
10use std::collections::HashMap;
11use uuid::Uuid;
12
13use super::device::{convert_manufacturer_data, convert_service_data, convert_services};
14use super::{AdapterId, CharacteristicId, DeviceId};
15
16#[derive(Clone, Debug, Eq, PartialEq)]
18pub enum BluetoothEvent {
19 Adapter {
21 id: AdapterId,
23 event: AdapterEvent,
25 },
26 Device {
28 id: DeviceId,
30 event: DeviceEvent,
32 },
33 Characteristic {
35 id: CharacteristicId,
37 event: CharacteristicEvent,
39 },
40}
41
42#[derive(Clone, Debug, Eq, PartialEq)]
44#[non_exhaustive]
45pub enum AdapterEvent {
46 Powered { powered: bool },
48 Discovering { discovering: bool },
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54#[non_exhaustive]
55pub enum DeviceEvent {
56 Discovered,
58 Connected { connected: bool },
60 Rssi { rssi: i16 },
62 ManufacturerData {
64 manufacturer_data: HashMap<u16, Vec<u8>>,
66 },
67 ServiceData {
69 service_data: HashMap<Uuid, Vec<u8>>,
71 },
72 Services {
74 services: Vec<Uuid>,
76 },
77 ServicesResolved,
79}
80
81#[derive(Clone, Debug, Eq, PartialEq)]
83#[non_exhaustive]
84pub enum CharacteristicEvent {
85 Value { value: Vec<u8> },
87}
88
89impl BluetoothEvent {
90 pub(crate) fn match_rules(
97 object: Option<impl Into<Path<'static>>>,
98 interfaces_added: bool,
99 ) -> Vec<MatchRule<'static>> {
100 let bus_name = "org.bluez".into();
103
104 let mut match_rules = vec![];
105
106 if interfaces_added {
109 let match_rule =
110 ObjectManagerInterfacesAdded::match_rule(Some(&bus_name), None).static_clone();
111 match_rules.push(match_rule);
112 }
113
114 let object_path = object.map(|o| o.into());
118 let mut match_rule =
119 PropertiesPropertiesChanged::match_rule(Some(&bus_name), object_path.as_ref())
120 .static_clone();
121 match_rule.path_is_namespace = true;
122 match_rules.push(match_rule);
123
124 match_rules
125 }
126
127 pub(crate) fn message_to_events(message: Message) -> Vec<BluetoothEvent> {
129 if let Some(properties_changed) = PropertiesPropertiesChanged::from_message(&message) {
130 let object_path = message.path().unwrap().into_static();
131 Self::properties_changed_to_events(object_path, properties_changed)
132 } else if let Some(interfaces_added) = ObjectManagerInterfacesAdded::from_message(&message)
133 {
134 Self::interfaces_added_to_events(interfaces_added)
135 } else {
136 log::info!("Unexpected message: {:?}", message);
137 vec![]
138 }
139 }
140
141 fn interfaces_added_to_events(
143 interfaces_added: ObjectManagerInterfacesAdded,
144 ) -> Vec<BluetoothEvent> {
145 log::trace!("InterfacesAdded: {:?}", interfaces_added);
146 let mut events = vec![];
147 let object_path = interfaces_added.object;
148 if let Some(_device) =
149 OrgBluezDevice1Properties::from_interfaces(&interfaces_added.interfaces)
150 {
151 let id = DeviceId { object_path };
152 events.push(BluetoothEvent::Device {
153 id,
154 event: DeviceEvent::Discovered,
155 })
156 }
157 events
158 }
159
160 fn properties_changed_to_events(
162 object_path: Path<'static>,
163 properties_changed: PropertiesPropertiesChanged,
164 ) -> Vec<BluetoothEvent> {
165 log::trace!(
166 "PropertiesChanged for {}: {:?}",
167 object_path,
168 properties_changed
169 );
170 let mut events = vec![];
171 let changed_properties = &properties_changed.changed_properties;
172 match properties_changed.interface_name.as_ref() {
173 ORG_BLUEZ_ADAPTER1_NAME => {
174 let id = AdapterId { object_path };
175 let adapter = OrgBluezAdapter1Properties(changed_properties);
176 if let Some(powered) = adapter.powered() {
177 events.push(BluetoothEvent::Adapter {
178 id: id.clone(),
179 event: AdapterEvent::Powered { powered },
180 })
181 }
182 if let Some(discovering) = adapter.discovering() {
183 events.push(BluetoothEvent::Adapter {
184 id,
185 event: AdapterEvent::Discovering { discovering },
186 });
187 }
188 }
189 ORG_BLUEZ_DEVICE1_NAME => {
190 let id = DeviceId { object_path };
191 let device = OrgBluezDevice1Properties(changed_properties);
192 if let Some(connected) = device.connected() {
193 events.push(BluetoothEvent::Device {
194 id: id.clone(),
195 event: DeviceEvent::Connected { connected },
196 });
197 }
198 if let Some(rssi) = device.rssi() {
199 events.push(BluetoothEvent::Device {
200 id: id.clone(),
201 event: DeviceEvent::Rssi { rssi },
202 });
203 }
204 if let Some(manufacturer_data) = device.manufacturer_data() {
205 events.push(BluetoothEvent::Device {
206 id: id.clone(),
207 event: DeviceEvent::ManufacturerData {
208 manufacturer_data: convert_manufacturer_data(manufacturer_data),
209 },
210 })
211 }
212 if let Some(service_data) = device.service_data() {
213 events.push(BluetoothEvent::Device {
214 id: id.clone(),
215 event: DeviceEvent::ServiceData {
216 service_data: convert_service_data(service_data),
217 },
218 })
219 }
220 if let Some(services) = device.uuids() {
221 events.push(BluetoothEvent::Device {
222 id: id.clone(),
223 event: DeviceEvent::Services {
224 services: convert_services(services),
225 },
226 })
227 }
228 if device.services_resolved() == Some(true) {
229 events.push(BluetoothEvent::Device {
230 id,
231 event: DeviceEvent::ServicesResolved,
232 });
233 }
234 }
235 ORG_BLUEZ_GATT_CHARACTERISTIC1_NAME => {
236 let id = CharacteristicId { object_path };
237 let characteristic = OrgBluezGattCharacteristic1Properties(changed_properties);
238 if let Some(value) = characteristic.value() {
239 events.push(BluetoothEvent::Characteristic {
240 id,
241 event: CharacteristicEvent::Value {
242 value: value.to_owned(),
243 },
244 })
245 }
246 }
247 _ => {}
248 }
249 events
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::super::ServiceId;
256 use crate::uuid_from_u32;
257 use dbus::arg::{PropMap, RefArg, Variant};
258
259 use super::*;
260
261 #[test]
262 fn adapter_powered() {
263 let message = adapter_powered_message("/org/bluez/hci0", true);
264 let id = AdapterId::new("/org/bluez/hci0");
265 assert_eq!(
266 BluetoothEvent::message_to_events(message),
267 vec![BluetoothEvent::Adapter {
268 id,
269 event: AdapterEvent::Powered { powered: true }
270 }]
271 )
272 }
273
274 #[test]
275 fn device_rssi() {
276 let rssi = 42;
277 let message = device_rssi_message("/org/bluez/hci0/dev_11_22_33_44_55_66", rssi);
278 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
279 assert_eq!(
280 BluetoothEvent::message_to_events(message),
281 vec![BluetoothEvent::Device {
282 id,
283 event: DeviceEvent::Rssi { rssi }
284 }]
285 )
286 }
287
288 #[test]
289 fn device_manufacturer_data() {
290 let mut manufacturer_data = HashMap::new();
291 manufacturer_data.insert(42, vec![1u8, 2, 3]);
292 let message = device_manufacturer_data_message(
293 "/org/bluez/hci0/dev_11_22_33_44_55_66",
294 manufacturer_data.clone(),
295 );
296 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
297 assert_eq!(
298 BluetoothEvent::message_to_events(message),
299 vec![BluetoothEvent::Device {
300 id,
301 event: DeviceEvent::ManufacturerData { manufacturer_data }
302 }]
303 )
304 }
305
306 #[test]
307 fn device_service_data() {
308 let mut service_data = HashMap::new();
309 service_data.insert(uuid_from_u32(0x11223344), vec![1u8, 2, 3]);
310 let message = device_service_data_message(
311 "/org/bluez/hci0/dev_11_22_33_44_55_66",
312 service_data.clone(),
313 );
314 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
315 assert_eq!(
316 BluetoothEvent::message_to_events(message),
317 vec![BluetoothEvent::Device {
318 id,
319 event: DeviceEvent::ServiceData { service_data }
320 }]
321 )
322 }
323
324 #[test]
325 fn device_services() {
326 let mut services = Vec::new();
327 services.push(uuid_from_u32(0x11223344));
328 let message =
329 device_services_message("/org/bluez/hci0/dev_11_22_33_44_55_66", services.clone());
330 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
331 assert_eq!(
332 BluetoothEvent::message_to_events(message),
333 vec![BluetoothEvent::Device {
334 id,
335 event: DeviceEvent::Services { services }
336 }]
337 )
338 }
339
340 #[test]
341 fn characteristic_value() {
342 let value: Vec<u8> = vec![1, 2, 3];
343 let message = characteristic_value_message(
344 "/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034",
345 &value,
346 );
347 let id =
348 CharacteristicId::new("/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034");
349 assert_eq!(
350 BluetoothEvent::message_to_events(message),
351 vec![BluetoothEvent::Characteristic {
352 id,
353 event: CharacteristicEvent::Value { value }
354 }]
355 )
356 }
357
358 #[test]
359 fn device_discovered() {
360 let message = new_device_message("/org/bluez/hci0/dev_11_22_33_44_55_66");
361 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
362 assert_eq!(
363 BluetoothEvent::message_to_events(message),
364 vec![BluetoothEvent::Device {
365 id,
366 event: DeviceEvent::Discovered
367 }]
368 )
369 }
370
371 #[test]
372 fn match_rules_all() {
373 let match_rules = BluetoothEvent::match_rules(None::<DeviceId>, true);
374
375 let message = new_device_message("/org/bluez/hci0/dev_11_22_33_44_55_66");
376 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
377
378 let message = adapter_powered_message("/org/bluez/hci0", true);
379 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
380
381 let message = device_rssi_message("/org/bluez/hci0/dev_11_22_33_44_55_66", 42);
382 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
383
384 let message = characteristic_value_message(
385 "/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034",
386 &vec![1, 2, 3],
387 );
388 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
389 }
390
391 #[test]
392 fn match_rules_device() {
393 let id = DeviceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66");
394 let match_rules = BluetoothEvent::match_rules(Some(id), false);
395
396 let message = new_device_message("/org/bluez/hci0/dev_11_22_33_44_55_66");
397 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
398
399 let message = adapter_powered_message("/org/bluez/hci0", true);
400 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
401
402 let message = device_rssi_message("/org/bluez/hci0/dev_11_22_33_44_55_66", 42);
403 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
404
405 let message = characteristic_value_message(
406 "/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034",
407 &vec![1, 2, 3],
408 );
409 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
410 }
411
412 #[test]
413 fn match_rules_service() {
414 let id = ServiceId::new("/org/bluez/hci0/dev_11_22_33_44_55_66/service0012");
415 let match_rules = BluetoothEvent::match_rules(Some(id), false);
416
417 let message = new_device_message("/org/bluez/hci0/dev_11_22_33_44_55_66");
418 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
419
420 let message = adapter_powered_message("/org/bluez/hci0", true);
421 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
422
423 let message = device_rssi_message("/org/bluez/hci0/dev_11_22_33_44_55_66", 42);
424 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
425
426 let message = characteristic_value_message(
427 "/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034",
428 &vec![1, 2, 3],
429 );
430 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
431 }
432
433 #[test]
434 fn match_rules_characteristic() {
435 let id =
436 CharacteristicId::new("/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034");
437 let match_rules = BluetoothEvent::match_rules(Some(id), false);
438
439 let message = new_device_message("/org/bluez/hci0/dev_11_22_33_44_55_66");
440 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
441
442 let message = adapter_powered_message("/org/bluez/hci0", true);
443 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
444
445 let message = device_rssi_message("/org/bluez/hci0/dev_11_22_33_44_55_66", 42);
446 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), false);
447
448 let message = characteristic_value_message(
449 "/org/bluez/hci0/dev_11_22_33_44_55_66/service0012/char0034",
450 &vec![1, 2, 3],
451 );
452 assert_eq!(match_rules.iter().any(|rule| rule.matches(&message)), true);
453 }
454
455 fn new_device_message(device_path: &'static str) -> Message {
456 let properties = HashMap::new();
457 let mut interfaces = HashMap::new();
458 interfaces.insert("org.bluez.Device1".to_string(), properties);
459 let interfaces_added = ObjectManagerInterfacesAdded {
460 object: device_path.into(),
461 interfaces,
462 };
463 interfaces_added.to_emit_message(&"/".into())
464 }
465
466 fn adapter_powered_message(adapter_path: &'static str, powered: bool) -> Message {
467 let mut changed_properties: PropMap = HashMap::new();
468 changed_properties.insert("Powered".to_string(), Variant(Box::new(powered)));
469 let properties_changed = PropertiesPropertiesChanged {
470 interface_name: "org.bluez.Adapter1".to_string(),
471 changed_properties,
472 invalidated_properties: vec![],
473 };
474 properties_changed.to_emit_message(&adapter_path.into())
475 }
476
477 fn device_rssi_message(device_path: &'static str, rssi: i16) -> Message {
478 let mut changed_properties: PropMap = HashMap::new();
479 changed_properties.insert("RSSI".to_string(), Variant(Box::new(rssi)));
480 let properties_changed = PropertiesPropertiesChanged {
481 interface_name: "org.bluez.Device1".to_string(),
482 changed_properties,
483 invalidated_properties: vec![],
484 };
485 properties_changed.to_emit_message(&device_path.into())
486 }
487
488 fn device_manufacturer_data_message(
489 device_path: &'static str,
490 manufacturer_data: HashMap<u16, Vec<u8>>,
491 ) -> Message {
492 let manufacturer_data: HashMap<_, _> = manufacturer_data
493 .into_iter()
494 .map::<(u16, Variant<Box<dyn RefArg>>), _>(|(k, v)| (k, Variant(Box::new(v))))
495 .collect();
496 let mut changed_properties: PropMap = HashMap::new();
497 changed_properties.insert(
498 "ManufacturerData".to_string(),
499 Variant(Box::new(manufacturer_data)),
500 );
501 let properties_changed = PropertiesPropertiesChanged {
502 interface_name: "org.bluez.Device1".to_string(),
503 changed_properties,
504 invalidated_properties: vec![],
505 };
506 properties_changed.to_emit_message(&device_path.into())
507 }
508
509 fn device_service_data_message(
510 device_path: &'static str,
511 service_data: HashMap<Uuid, Vec<u8>>,
512 ) -> Message {
513 let service_data: HashMap<_, _> = service_data
514 .into_iter()
515 .map::<(String, Variant<Box<dyn RefArg>>), _>(|(k, v)| {
516 (k.to_string(), Variant(Box::new(v)))
517 })
518 .collect();
519 let mut changed_properties: HashMap<String, Variant<Box<dyn RefArg>>> = HashMap::new();
520 changed_properties.insert("ServiceData".to_string(), Variant(Box::new(service_data)));
521 let properties_changed = PropertiesPropertiesChanged {
522 interface_name: "org.bluez.Device1".to_string(),
523 changed_properties,
524 invalidated_properties: vec![],
525 };
526 properties_changed.to_emit_message(&device_path.into())
527 }
528
529 fn device_services_message(device_path: &'static str, services: Vec<Uuid>) -> Message {
530 let services: Vec<_> = services
531 .into_iter()
532 .map::<String, _>(|k| k.to_string())
533 .collect();
534 let mut changed_properties: HashMap<String, Variant<Box<dyn RefArg>>> = HashMap::new();
535 changed_properties.insert("UUIDs".to_string(), Variant(Box::new(services)));
536 let properties_changed = PropertiesPropertiesChanged {
537 interface_name: "org.bluez.Device1".to_string(),
538 changed_properties,
539 invalidated_properties: vec![],
540 };
541 properties_changed.to_emit_message(&device_path.into())
542 }
543
544 fn characteristic_value_message(characteristic_path: &'static str, value: &[u8]) -> Message {
545 let mut changed_properties: PropMap = HashMap::new();
546 changed_properties.insert("Value".to_string(), Variant(Box::new(value.to_owned())));
547 let properties_changed = PropertiesPropertiesChanged {
548 interface_name: "org.bluez.GattCharacteristic1".to_string(),
549 changed_properties,
550 invalidated_properties: vec![],
551 };
552 properties_changed.to_emit_message(&characteristic_path.into())
553 }
554}