From 24c140bc82cb3579a4a40a32b036ecdb1c9f19ac Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Wed, 5 Sep 2018 08:36:41 +0300 Subject: [PATCH 01/12] use a single dbus connection, wrapped by BluetoothSession use BluetoothSession for OBEX --- Cargo.toml | 2 +- examples/test.rs | 10 +- examples/test2.rs | 30 +++--- examples/test3.rs | 21 +++-- examples/test4.rs | 15 +-- src/bluetooth_adapter.rs | 105 +++++++++++++-------- src/bluetooth_device.rs | 133 +++++++++++++++++---------- src/bluetooth_discovery_session.rs | 1 - src/bluetooth_gatt_characteristic.rs | 105 +++++++++++++-------- src/bluetooth_gatt_descriptor.rs | 103 +++++++++++++-------- src/bluetooth_gatt_service.rs | 33 ++++--- src/bluetooth_obex.rs | 36 ++++---- src/bluetooth_session.rs | 120 ++++++++++++++++++++++++ src/bluetooth_utils.rs | 84 +++++++++++------ src/lib.rs | 2 + 15 files changed, 544 insertions(+), 256 deletions(-) create mode 100644 src/bluetooth_session.rs diff --git a/Cargo.toml b/Cargo.toml index 7052435..a75f028 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blurz" -version = "0.3.0" +version = "0.4.0" description = "Bluetooth lib for Rust using blueZ/dbus" readme = "README.md" authors = ["Attila Dusnoki "] diff --git a/examples/test.rs b/examples/test.rs index 20c5bc7..696d456 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -2,11 +2,13 @@ extern crate blurz; use std::error::Error; +use blurz::bluetooth_session::BluetoothSession as Session; use blurz::bluetooth_adapter::BluetoothAdapter as Adapter; use blurz::bluetooth_device::BluetoothDevice as Device; fn test() -> Result<(), Box> { - let adapter: Adapter = try!(Adapter::init()); + let session = &Session::create_session().unwrap(); + let adapter: Adapter = try!(Adapter::init(session)); let device: Device = try!(adapter.get_first_device()); println!("{:?}", device); Ok(()) @@ -14,7 +16,7 @@ fn test() -> Result<(), Box> { fn main() { match test() { - Ok(_) => (), - Err(e) => println!("{:?}", e), - } + Ok(_) => (), + Err(e) => println!("{:?}", e), + } } diff --git a/examples/test2.rs b/examples/test2.rs index 6b73312..8fdd144 100644 --- a/examples/test2.rs +++ b/examples/test2.rs @@ -4,18 +4,20 @@ static BATTERY_SERVICE_UUID: &'static str = "0000180f-0000-1000-8000-00805f9b34f static COLOR_PICKER_SERVICE_UUID: &'static str = "00001812-0000-1000-8000-00805f9b34fb"; use std::error::Error; -use std::time::Duration; use std::thread; +use std::time::Duration; use blurz::bluetooth_adapter::BluetoothAdapter as Adapter; use blurz::bluetooth_device::BluetoothDevice as Device; -use blurz::bluetooth_gatt_service::BluetoothGATTService as Service; +use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as DiscoverySession; use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic as Characteristic; use blurz::bluetooth_gatt_descriptor::BluetoothGATTDescriptor as Descriptor; -use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as DiscoverySession; +use blurz::bluetooth_gatt_service::BluetoothGATTService as Service; +use blurz::bluetooth_session::BluetoothSession as Session; fn test2() -> Result<(), Box> { - let adapter: Adapter = try!(Adapter::init()); + let bt_session = &Session::create_session()?; + let adapter: Adapter = try!(Adapter::init(bt_session)); let session = try!(DiscoverySession::create_session(adapter.get_id())); try!(session.start_discovery()); //let mut devices = vec!(); @@ -32,15 +34,14 @@ fn test2() -> Result<(), Box> { return Err(Box::from("No device found")); } println!("{} device(s) found", devices.len()); - let mut device: Device = Device::new("".to_string()); + let mut device: Device = Device::new(bt_session, "".to_string()); 'device_loop: for d in devices { - device = Device::new(d.clone()); + device = Device::new(bt_session, d.clone()); println!("{} {:?}", device.get_id(), device.get_alias()); let uuids = try!(device.get_uuids()); println!("{:?}", uuids); 'uuid_loop: for uuid in uuids { - if uuid == COLOR_PICKER_SERVICE_UUID || - uuid == BATTERY_SERVICE_UUID { + if uuid == COLOR_PICKER_SERVICE_UUID || uuid == BATTERY_SERVICE_UUID { println!("{:?} has a service!", device.get_alias()); println!("connect device..."); device.connect().ok(); @@ -66,20 +67,19 @@ fn test2() -> Result<(), Box> { } let services = try!(device.get_gatt_services()); for service in services { - let s = Service::new(service.clone()); + let s = Service::new(bt_session, service.clone()); println!("{:?}", s); let characteristics = try!(s.get_gatt_characteristics()); for characteristic in characteristics { - let c = Characteristic::new(characteristic.clone()); + let c = Characteristic::new(bt_session, characteristic.clone()); println!("{:?}", c); println!("Value: {:?}", c.read_value(None)); let descriptors = try!(c.get_gatt_descriptors()); for descriptor in descriptors { - let d = Descriptor::new(descriptor.clone()); + let d = Descriptor::new(bt_session, descriptor.clone()); println!("{:?}", d); println!("Value: {:?}", d.read_value(None)); } - } } try!(device.disconnect()); @@ -88,7 +88,7 @@ fn test2() -> Result<(), Box> { fn main() { match test2() { - Ok(_) => (), - Err(e) => println!("{:?}", e), - } + Ok(_) => (), + Err(e) => println!("{:?}", e), + } } diff --git a/examples/test3.rs b/examples/test3.rs index 28677ba..adb463d 100644 --- a/examples/test3.rs +++ b/examples/test3.rs @@ -1,15 +1,17 @@ extern crate blurz; use std::error::Error; -use std::time::Duration; use std::thread; +use std::time::Duration; use blurz::bluetooth_adapter::BluetoothAdapter as Adapter; use blurz::bluetooth_device::BluetoothDevice as Device; use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as DiscoverySession; +use blurz::bluetooth_session::BluetoothSession as Session; fn test3() -> Result<(), Box> { - let adapter: Adapter = try!(Adapter::init()); + let bt_session = &Session::create_session()?; + let adapter: Adapter = try!(Adapter::init(bt_session)); try!(adapter.set_powered(true)); loop { let session = try!(DiscoverySession::create_session(adapter.get_id())); @@ -20,8 +22,13 @@ fn test3() -> Result<(), Box> { println!("{} device(s) found", devices.len()); 'device_loop: for d in devices { - let device = Device::new(d.clone()); - println!("{} {:?} {:?}", device.get_id(), device.get_address(),device.get_rssi()); + let device = Device::new(bt_session, d.clone()); + println!( + "{} {:?} {:?}", + device.get_id(), + device.get_address(), + device.get_rssi() + ); try!(adapter.remove_device(device.get_id())); } try!(session.stop_discovery()); @@ -30,7 +37,7 @@ fn test3() -> Result<(), Box> { fn main() { match test3() { - Ok(_) => (), - Err(e) => println!("{:?}", e), - } + Ok(_) => (), + Err(e) => println!("{:?}", e), + } } diff --git a/examples/test4.rs b/examples/test4.rs index 8787969..6f005e2 100644 --- a/examples/test4.rs +++ b/examples/test4.rs @@ -8,28 +8,29 @@ use std::path::Path; use blurz::bluetooth_adapter::BluetoothAdapter as Adapter; use blurz::bluetooth_device::BluetoothDevice as Device; -use blurz::bluetooth_obex::open_bus_connection; use blurz::bluetooth_obex::{ BluetoothOBEXSession as OBEXSession, BluetoothOBEXTransfer as OBEXTransfer, }; +use blurz::bluetooth_session::BluetoothSession as Session; fn test_obex_file_transfer() -> Result<(), Box> { - let adapter: Adapter = Adapter::init()?; + let session = &Session::create_session()?; + let adapter: Adapter = Adapter::init(session)?; let devices: Vec = adapter.get_device_list()?; let filtered_devices = devices .iter() .filter(|&device_id| { - let device = Device::new(device_id.to_string()); + let device = Device::new(session, device_id.to_string()); device.is_ready_to_receive().unwrap() - }).cloned() + }) + .cloned() .collect::>(); let device_id: &str = &filtered_devices[0]; - let device = Device::new(device_id.to_string()); + let device = Device::new(session, device_id.to_string()); - let connection = open_bus_connection()?; - let session = OBEXSession::new(connection, &device)?; + let session = OBEXSession::new(session, &device)?; let mut empty_file = File::create("./test.png")?; empty_file.write_all(b"1111")?; diff --git a/src/bluetooth_adapter.rs b/src/bluetooth_adapter.rs index 6353c10..18cfdd9 100644 --- a/src/bluetooth_adapter.rs +++ b/src/bluetooth_adapter.rs @@ -1,4 +1,5 @@ use bluetooth_device::BluetoothDevice; +use bluetooth_session::BluetoothSession; use bluetooth_utils; use dbus::MessageItem; use hex::FromHex; @@ -6,34 +7,39 @@ use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; -#[derive(Clone, Debug)] -pub struct BluetoothAdapter { +#[derive(Clone)] +pub struct BluetoothAdapter<'a> { object_path: String, + session: &'a BluetoothSession, } -impl BluetoothAdapter { - fn new(object_path: String) -> BluetoothAdapter { +impl<'a> BluetoothAdapter<'a> { + fn new(session: &'a BluetoothSession, object_path: String) -> BluetoothAdapter<'a> { BluetoothAdapter { object_path: object_path, + session: session, } } - pub fn init() -> Result> { - let adapters = try!(bluetooth_utils::get_adapters()); + pub fn init(session: &BluetoothSession) -> Result> { + let adapters = try!(bluetooth_utils::get_adapters(session.get_connection())); if adapters.is_empty() { - return Err(Box::from("Bluetooth adapter not found")) + return Err(Box::from("Bluetooth adapter not found")); } - Ok(BluetoothAdapter::new(adapters[0].clone())) + Ok(BluetoothAdapter::new(session, adapters[0].clone())) } - pub fn create_adapter(object_path: String) -> Result> { - let adapters = try!(bluetooth_utils::get_adapters()); + pub fn create_adapter( + session: &BluetoothSession, + object_path: String, + ) -> Result> { + let adapters = try!(bluetooth_utils::get_adapters(session.get_connection())); for adapter in adapters { if adapter == object_path { - return Ok(BluetoothAdapter::new(adapter.clone())); + return Ok(BluetoothAdapter::new(session, adapter.clone())); } } Err(Box::from("Bluetooth adapter not found")) @@ -44,34 +50,56 @@ impl BluetoothAdapter { } pub fn get_first_device(&self) -> Result> { - let devices = try!(bluetooth_utils::list_devices(&self.object_path)); + let devices = try!(bluetooth_utils::list_devices( + self.session.get_connection(), + &self.object_path + )); if devices.is_empty() { - return Err(Box::from("No device found.")) + return Err(Box::from("No device found.")); } - Ok(BluetoothDevice::new(devices[0].clone())) + Ok(BluetoothDevice::new(self.session, devices[0].clone())) } pub fn get_device_list(&self) -> Result, Box> { - bluetooth_utils::list_devices(&self.object_path) + bluetooth_utils::list_devices(self.session.get_connection(), &self.object_path) } fn get_property(&self, prop: &str) -> Result> { - bluetooth_utils::get_property(ADAPTER_INTERFACE, &self.object_path, prop) + bluetooth_utils::get_property( + self.session.get_connection(), + ADAPTER_INTERFACE, + &self.object_path, + prop, + ) } fn set_property(&self, prop: &str, value: T) -> Result<(), Box> - where T: Into { - bluetooth_utils::set_property(ADAPTER_INTERFACE, &self.object_path, prop, value) + where + T: Into, + { + bluetooth_utils::set_property( + self.session.get_connection(), + ADAPTER_INTERFACE, + &self.object_path, + prop, + value, + ) } fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { - bluetooth_utils::call_method(ADAPTER_INTERFACE, &self.object_path, method, param) + bluetooth_utils::call_method( + self.session.get_connection(), + ADAPTER_INTERFACE, + &self.object_path, + method, + param, + ) } -/* - * Properties - */ + /* + * Properties + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n108 pub fn get_address(&self) -> Result> { @@ -109,7 +137,7 @@ impl BluetoothAdapter { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n147 - pub fn set_powered(&self, value: bool) -> Result<(),Box> { + pub fn set_powered(&self, value: bool) -> Result<(), Box> { self.set_property("Powered", value) } @@ -175,7 +203,7 @@ impl BluetoothAdapter { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n215 - pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { let modalias = try!(self.get_property("Modalias")); let m = modalias.inner::<&str>().unwrap(); let ids: Vec<&str> = m.split(":").collect(); @@ -185,35 +213,37 @@ impl BluetoothAdapter { let product = Vec::from_hex(ids[1][6..10].to_string()).unwrap(); let device = Vec::from_hex(ids[1][11..15].to_string()).unwrap(); - Ok((source, - (vendor[0] as u32) * 16 * 16 + (vendor[1] as u32), - (product[0] as u32) * 16 * 16 + (product[1] as u32), - (device[0] as u32) * 16 * 16 + (device[1] as u32))) + Ok(( + source, + (vendor[0] as u32) * 16 * 16 + (vendor[1] as u32), + (product[0] as u32) * 16 * 16 + (product[1] as u32), + (device[0] as u32) * 16 * 16 + (device[1] as u32), + )) } pub fn get_vendor_id_source(&self) -> Result> { - let (vendor_id_source,_,_,_) = try!(self.get_modalias()); + let (vendor_id_source, _, _, _) = try!(self.get_modalias()); Ok(vendor_id_source) } pub fn get_vendor_id(&self) -> Result> { - let (_,vendor_id,_,_) = try!(self.get_modalias()); + let (_, vendor_id, _, _) = try!(self.get_modalias()); Ok(vendor_id) } pub fn get_product_id(&self) -> Result> { - let (_,_,product_id,_) = try!(self.get_modalias()); + let (_, _, product_id, _) = try!(self.get_modalias()); Ok(product_id) } pub fn get_device_id(&self) -> Result> { - let (_,_,_,device_id) = try!(self.get_modalias()); + let (_, _, _, device_id) = try!(self.get_modalias()); Ok(device_id) } -/* - * Methods - */ + /* + * Methods + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n12 pub fn start_discovery(&self) -> Result<(), Box> { @@ -227,6 +257,9 @@ impl BluetoothAdapter { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n40 pub fn remove_device(&self, device: String) -> Result<(), Box> { - self.call_method("RemoveDevice", Some(&[MessageItem::ObjectPath(device.into())])) + self.call_method( + "RemoveDevice", + Some(&[MessageItem::ObjectPath(device.into())]), + ) } } diff --git a/src/bluetooth_device.rs b/src/bluetooth_device.rs index 1a79ad4..11aad23 100644 --- a/src/bluetooth_device.rs +++ b/src/bluetooth_device.rs @@ -1,3 +1,4 @@ +use bluetooth_session::BluetoothSession; use bluetooth_utils; use dbus::MessageItem; use hex::FromHex; @@ -6,16 +7,17 @@ use std::error::Error; static DEVICE_INTERFACE: &'static str = "org.bluez.Device1"; -#[derive(Clone, Debug)] -pub struct BluetoothDevice { +#[derive(Clone)] +pub struct BluetoothDevice<'a> { object_path: String, + session: &'a BluetoothSession, } -impl BluetoothDevice { - pub fn new(object_path: String) - -> BluetoothDevice { +impl<'a> BluetoothDevice<'a> { + pub fn new(session: &'a BluetoothSession, object_path: String) -> BluetoothDevice { BluetoothDevice { - object_path: object_path + object_path: object_path, + session: session, } } @@ -24,21 +26,40 @@ impl BluetoothDevice { } fn get_property(&self, prop: &str) -> Result> { - bluetooth_utils::get_property(DEVICE_INTERFACE, &self.object_path, prop) + bluetooth_utils::get_property( + self.session.get_connection(), + DEVICE_INTERFACE, + &self.object_path, + prop, + ) } fn set_property(&self, prop: &str, value: T) -> Result<(), Box> - where T: Into { - bluetooth_utils::set_property(DEVICE_INTERFACE, &self.object_path, prop, value) + where + T: Into, + { + bluetooth_utils::set_property( + self.session.get_connection(), + DEVICE_INTERFACE, + &self.object_path, + prop, + value, + ) } fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { - bluetooth_utils::call_method(DEVICE_INTERFACE, &self.object_path, method, param) - } - -/* - * Properties - */ + bluetooth_utils::call_method( + self.session.get_connection(), + DEVICE_INTERFACE, + &self.object_path, + method, + param, + ) + } + + /* + * Properties + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n105 pub fn get_address(&self) -> Result> { let address = try!(self.get_property("Address")); @@ -82,24 +103,24 @@ impl BluetoothDevice { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n139 pub fn is_paired(&self) -> Result> { - let paired = try!(self.get_property("Paired")); - Ok(paired.inner::().unwrap()) + let paired = try!(self.get_property("Paired")); + Ok(paired.inner::().unwrap()) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n143 pub fn is_connected(&self) -> Result> { - let connected = try!(self.get_property("Connected")); - Ok(connected.inner::().unwrap()) + let connected = try!(self.get_property("Connected")); + Ok(connected.inner::().unwrap()) } pub fn is_ready_to_receive(&self) -> Option { let is_connected: bool = match self.is_connected() { Ok(value) => value, - Err(_) => false + Err(_) => false, }; let is_paired: bool = match self.is_paired() { Ok(value) => value, - Err(_) => false + Err(_) => false, }; Some(is_paired & is_connected) } @@ -128,7 +149,7 @@ impl BluetoothDevice { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n161 - pub fn set_alias(&self, value: String) -> Result<(),Box> { + pub fn set_alias(&self, value: String) -> Result<(), Box> { self.set_property("Alias", value) } @@ -145,7 +166,7 @@ impl BluetoothDevice { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n189 - pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { let modalias = try!(self.get_property("Modalias")); let m = modalias.inner::<&str>().unwrap(); let ids: Vec<&str> = m.split(":").collect(); @@ -155,29 +176,31 @@ impl BluetoothDevice { let product = Vec::from_hex(ids[1][6..10].to_string()).unwrap(); let device = Vec::from_hex(ids[1][11..15].to_string()).unwrap(); - Ok((source, - (vendor[0] as u32) * 16 * 16 + (vendor[1] as u32), - (product[0] as u32) * 16 * 16 + (product[1] as u32), - (device[0] as u32) * 16 * 16 + (device[1] as u32))) + Ok(( + source, + (vendor[0] as u32) * 16 * 16 + (vendor[1] as u32), + (product[0] as u32) * 16 * 16 + (product[1] as u32), + (device[0] as u32) * 16 * 16 + (device[1] as u32), + )) } pub fn get_vendor_id_source(&self) -> Result> { - let (vendor_id_source,_,_,_) = try!(self.get_modalias()); + let (vendor_id_source, _, _, _) = try!(self.get_modalias()); Ok(vendor_id_source) } pub fn get_vendor_id(&self) -> Result> { - let (_,vendor_id,_,_) = try!(self.get_modalias()); + let (_, vendor_id, _, _) = try!(self.get_modalias()); Ok(vendor_id) } pub fn get_product_id(&self) -> Result> { - let (_,_,product_id,_) = try!(self.get_modalias()); + let (_, _, product_id, _) = try!(self.get_modalias()); Ok(product_id) } pub fn get_device_id(&self) -> Result> { - let (_,_,_,device_id) = try!(self.get_modalias()); + let (_, _, _, device_id) = try!(self.get_modalias()); Ok(device_id) } @@ -197,14 +220,19 @@ impl BluetoothDevice { pub fn get_manufacturer_data(&self) -> Result>, Box> { let manufacturer_data_array = try!(self.get_property("ManufacturerData")); let mut m = HashMap::new(); - let dict_vec = manufacturer_data_array.inner::<&Vec>().unwrap(); + let dict_vec = manufacturer_data_array + .inner::<&Vec>() + .unwrap(); for dict in dict_vec { let (key, value) = dict.inner::<(&MessageItem, &MessageItem)>().unwrap(); - let v = value.inner::<&MessageItem>().unwrap() - .inner::<&Vec>().unwrap() - .into_iter() - .map(|b| b.inner::().unwrap_or(0)) - .collect(); + let v = value + .inner::<&MessageItem>() + .unwrap() + .inner::<&Vec>() + .unwrap() + .into_iter() + .map(|b| b.inner::().unwrap_or(0)) + .collect(); m.insert(key.inner::().unwrap(), v); } Ok(m) @@ -217,11 +245,14 @@ impl BluetoothDevice { let dict_vec = service_data_array.inner::<&Vec>().unwrap(); for dict in dict_vec { let (key, value) = dict.inner::<(&MessageItem, &MessageItem)>().unwrap(); - let v = value.inner::<&MessageItem>().unwrap() - .inner::<&Vec>().unwrap() - .into_iter() - .map(|b| b.inner::().unwrap_or(0)) - .collect(); + let v = value + .inner::<&MessageItem>() + .unwrap() + .inner::<&Vec>() + .unwrap() + .into_iter() + .map(|b| b.inner::().unwrap_or(0)) + .collect(); m.insert(key.inner::<&str>().unwrap().to_string(), v); } Ok(m) @@ -229,12 +260,12 @@ impl BluetoothDevice { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n215 pub fn get_gatt_services(&self) -> Result, Box> { - bluetooth_utils::list_services(&self.object_path) + bluetooth_utils::list_services(self.session.get_connection(), &self.object_path) } -/* - * Methods - */ + /* + * Methods + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n12 pub fn connect(&self) -> Result<(), Box> { @@ -242,27 +273,27 @@ impl BluetoothDevice { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n29 - pub fn disconnect(&self) -> Result<(), Box>{ + pub fn disconnect(&self) -> Result<(), Box> { self.call_method("Disconnect", None) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n43 - pub fn connect_profile(&self, uuid: String) -> Result<(), Box>{ + pub fn connect_profile(&self, uuid: String) -> Result<(), Box> { self.call_method("ConnectProfile", Some(&[uuid.into()])) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n55 - pub fn disconnect_profile(&self, uuid: String) -> Result<(), Box>{ + pub fn disconnect_profile(&self, uuid: String) -> Result<(), Box> { self.call_method("DisconnectProfile", Some(&[uuid.into()])) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n70 - pub fn pair(&self) -> Result<(), Box>{ + pub fn pair(&self) -> Result<(), Box> { self.call_method("Pair", None) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n97 - pub fn cancel_pairing(&self) -> Result<(), Box>{ + pub fn cancel_pairing(&self) -> Result<(), Box> { self.call_method("CancelPairing", None) } } diff --git a/src/bluetooth_discovery_session.rs b/src/bluetooth_discovery_session.rs index bbf4d04..2d23e9e 100644 --- a/src/bluetooth_discovery_session.rs +++ b/src/bluetooth_discovery_session.rs @@ -4,7 +4,6 @@ use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; static SERVICE_NAME: &'static str = "org.bluez"; -#[derive(Debug)] pub struct BluetoothDiscoverySession { adapter: String, connection: Connection, diff --git a/src/bluetooth_gatt_characteristic.rs b/src/bluetooth_gatt_characteristic.rs index 9b181a5..57f6966 100644 --- a/src/bluetooth_gatt_characteristic.rs +++ b/src/bluetooth_gatt_characteristic.rs @@ -1,21 +1,23 @@ -use dbus::{Connection, BusType, Message, MessageItem, MessageItemArray, Signature}; +use bluetooth_session::BluetoothSession; use bluetooth_utils; +use dbus::{BusType, Connection, Message, MessageItem, MessageItemArray, Signature}; use std::error::Error; static SERVICE_NAME: &'static str = "org.bluez"; static GATT_CHARACTERISTIC_INTERFACE: &'static str = "org.bluez.GattCharacteristic1"; -#[derive(Clone, Debug)] -pub struct BluetoothGATTCharacteristic { +#[derive(Clone)] +pub struct BluetoothGATTCharacteristic<'a> { object_path: String, + session: &'a BluetoothSession, } -impl BluetoothGATTCharacteristic { - pub fn new(object_path: String) - -> BluetoothGATTCharacteristic { +impl<'a> BluetoothGATTCharacteristic<'a> { + pub fn new(session: &'a BluetoothSession, object_path: String) -> BluetoothGATTCharacteristic { BluetoothGATTCharacteristic { - object_path: object_path + object_path: object_path, + session: session, } } @@ -24,16 +26,27 @@ impl BluetoothGATTCharacteristic { } fn get_property(&self, prop: &str) -> Result> { - bluetooth_utils::get_property(GATT_CHARACTERISTIC_INTERFACE, &self.object_path, prop) + bluetooth_utils::get_property( + self.session.get_connection(), + GATT_CHARACTERISTIC_INTERFACE, + &self.object_path, + prop, + ) } fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { - bluetooth_utils::call_method(GATT_CHARACTERISTIC_INTERFACE, &self.object_path, method, param) + bluetooth_utils::call_method( + self.session.get_connection(), + GATT_CHARACTERISTIC_INTERFACE, + &self.object_path, + method, + param, + ) } -/* - * Properties - */ + /* + * Properties + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n114 pub fn get_uuid(&self) -> Result> { @@ -77,28 +90,34 @@ impl BluetoothGATTCharacteristic { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n156 pub fn get_gatt_descriptors(&self) -> Result, Box> { - bluetooth_utils::list_descriptors(&self.object_path) + bluetooth_utils::list_descriptors(self.session.get_connection(), &self.object_path) } -/* - * Methods - */ + /* + * Methods + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n72 pub fn read_value(&self, offset: Option) -> Result, Box> { let c = try!(Connection::get_private(BusType::System)); - let mut m = try!(Message::new_method_call(SERVICE_NAME, &self.object_path, GATT_CHARACTERISTIC_INTERFACE, "ReadValue")); - m.append_items(&[ - MessageItem::Array( - MessageItemArray::new( - match offset { - Some(o) => vec![MessageItem::DictEntry(Box::new("offset".into()), - Box::new(MessageItem::Variant(Box::new(o.into()))))], - None => vec![] - }, Signature::from("a{sv}") - ).unwrap() - ) - ]); + let mut m = try!(Message::new_method_call( + SERVICE_NAME, + &self.object_path, + GATT_CHARACTERISTIC_INTERFACE, + "ReadValue" + )); + m.append_items(&[MessageItem::Array( + MessageItemArray::new( + match offset { + Some(o) => vec![MessageItem::DictEntry( + Box::new("offset".into()), + Box::new(MessageItem::Variant(Box::new(o.into()))), + )], + None => vec![], + }, + Signature::from("a{sv}"), + ).unwrap(), + )]); let reply = try!(c.send_with_reply_and_block(m, 1000)); let items: MessageItem = reply.get1().unwrap(); let z: &[MessageItem] = items.inner().unwrap(); @@ -118,18 +137,24 @@ impl BluetoothGATTCharacteristic { } res }; - self.call_method("WriteValue", Some(&[ - MessageItem::new_array(values_msgs).unwrap(), - MessageItem::Array( - MessageItemArray::new( - match offset { - Some(o) => vec![MessageItem::DictEntry(Box::new("offset".into()), - Box::new(MessageItem::Variant(Box::new(o.into()))))], - None => vec![] - }, Signature::from("a{sv}") - ).unwrap() - ) - ])) + self.call_method( + "WriteValue", + Some(&[ + MessageItem::new_array(values_msgs).unwrap(), + MessageItem::Array( + MessageItemArray::new( + match offset { + Some(o) => vec![MessageItem::DictEntry( + Box::new("offset".into()), + Box::new(MessageItem::Variant(Box::new(o.into()))), + )], + None => vec![], + }, + Signature::from("a{sv}"), + ).unwrap(), + ), + ]), + ) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n96 diff --git a/src/bluetooth_gatt_descriptor.rs b/src/bluetooth_gatt_descriptor.rs index 876b843..bc64a4e 100644 --- a/src/bluetooth_gatt_descriptor.rs +++ b/src/bluetooth_gatt_descriptor.rs @@ -1,21 +1,23 @@ -use dbus::{Connection, BusType, Message, MessageItem, MessageItemArray, Signature}; +use bluetooth_session::BluetoothSession; use bluetooth_utils; +use dbus::{BusType, Connection, Message, MessageItem, MessageItemArray, Signature}; use std::error::Error; static SERVICE_NAME: &'static str = "org.bluez"; static GATT_DESCRIPTOR_INTERFACE: &'static str = "org.bluez.GattDescriptor1"; -#[derive(Clone, Debug)] -pub struct BluetoothGATTDescriptor { +#[derive(Clone)] +pub struct BluetoothGATTDescriptor<'a> { object_path: String, + session: &'a BluetoothSession, } -impl BluetoothGATTDescriptor { - pub fn new(object_path: String) - -> BluetoothGATTDescriptor { +impl<'a> BluetoothGATTDescriptor<'a> { + pub fn new(session: &'a BluetoothSession, object_path: String) -> BluetoothGATTDescriptor { BluetoothGATTDescriptor { - object_path: object_path + object_path: object_path, + session: session, } } @@ -24,16 +26,27 @@ impl BluetoothGATTDescriptor { } fn get_property(&self, prop: &str) -> Result> { - bluetooth_utils::get_property(GATT_DESCRIPTOR_INTERFACE, &self.object_path, prop) + bluetooth_utils::get_property( + self.session.get_connection(), + GATT_DESCRIPTOR_INTERFACE, + &self.object_path, + prop, + ) } fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { - bluetooth_utils::call_method(GATT_DESCRIPTOR_INTERFACE, &self.object_path, method, param) + bluetooth_utils::call_method( + self.session.get_connection(), + GATT_DESCRIPTOR_INTERFACE, + &self.object_path, + method, + param, + ) } -/* - * Properties - */ + /* + * Properties + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n198 pub fn get_uuid(&self) -> Result> { @@ -69,25 +82,31 @@ impl BluetoothGATTDescriptor { Ok(v) } -/* - * Methods - */ + /* + * Methods + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n174 pub fn read_value(&self, offset: Option) -> Result, Box> { let c = try!(Connection::get_private(BusType::System)); - let mut m = try!(Message::new_method_call(SERVICE_NAME, &self.object_path, GATT_DESCRIPTOR_INTERFACE, "ReadValue")); - m.append_items(&[ - MessageItem::Array( - MessageItemArray::new( - match offset { - Some(o) => vec![MessageItem::DictEntry(Box::new("offset".into()), - Box::new(MessageItem::Variant(Box::new(o.into()))))], - None => vec![] - }, Signature::from("a{sv}") - ).unwrap() - ) - ]); + let mut m = try!(Message::new_method_call( + SERVICE_NAME, + &self.object_path, + GATT_DESCRIPTOR_INTERFACE, + "ReadValue" + )); + m.append_items(&[MessageItem::Array( + MessageItemArray::new( + match offset { + Some(o) => vec![MessageItem::DictEntry( + Box::new("offset".into()), + Box::new(MessageItem::Variant(Box::new(o.into()))), + )], + None => vec![], + }, + Signature::from("a{sv}"), + ).unwrap(), + )]); let reply = try!(c.send_with_reply_and_block(m, 1000)); let items: MessageItem = reply.get1().unwrap(); let z: &[MessageItem] = items.inner().unwrap(); @@ -107,17 +126,23 @@ impl BluetoothGATTDescriptor { } res }; - self.call_method("WriteValue", Some(&[ - MessageItem::new_array(args).unwrap(), - MessageItem::Array( - MessageItemArray::new( - match offset { - Some(o) => vec![MessageItem::DictEntry(Box::new("offset".into()), - Box::new(MessageItem::Variant(Box::new(o.into()))))], - None => vec![] - }, Signature::from("a{sv}") - ).unwrap() - ) - ])) + self.call_method( + "WriteValue", + Some(&[ + MessageItem::new_array(args).unwrap(), + MessageItem::Array( + MessageItemArray::new( + match offset { + Some(o) => vec![MessageItem::DictEntry( + Box::new("offset".into()), + Box::new(MessageItem::Variant(Box::new(o.into()))), + )], + None => vec![], + }, + Signature::from("a{sv}"), + ).unwrap(), + ), + ]), + ) } } diff --git a/src/bluetooth_gatt_service.rs b/src/bluetooth_gatt_service.rs index d79e02a..e076522 100644 --- a/src/bluetooth_gatt_service.rs +++ b/src/bluetooth_gatt_service.rs @@ -1,20 +1,22 @@ -use dbus::{MessageItem}; +use bluetooth_session::BluetoothSession; use bluetooth_utils; +use dbus::MessageItem; use std::error::Error; static GATT_SERVICE_INTERFACE: &'static str = "org.bluez.GattService1"; -#[derive(Clone, Debug)] -pub struct BluetoothGATTService { +#[derive(Clone)] +pub struct BluetoothGATTService<'a> { object_path: String, + session: &'a BluetoothSession, } -impl BluetoothGATTService { - pub fn new(object_path: String) - -> BluetoothGATTService { +impl<'a> BluetoothGATTService<'a> { + pub fn new(session: &'a BluetoothSession, object_path: String) -> BluetoothGATTService { BluetoothGATTService { - object_path: object_path + object_path: object_path, + session: session, } } @@ -23,12 +25,17 @@ impl BluetoothGATTService { } fn get_property(&self, prop: &str) -> Result> { - bluetooth_utils::get_property(GATT_SERVICE_INTERFACE, &self.object_path, prop) + bluetooth_utils::get_property( + self.session.get_connection(), + GATT_SERVICE_INTERFACE, + &self.object_path, + prop, + ) } -/* - * Properties - */ + /* + * Properties + */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n33 pub fn get_uuid(&self) -> Result> { @@ -53,7 +60,7 @@ impl BluetoothGATTService { Err(Box::from("Not implemented")) } - pub fn get_gatt_characteristics(&self) -> Result,Box> { - bluetooth_utils::list_characteristics(&self.object_path) + pub fn get_gatt_characteristics(&self) -> Result, Box> { + bluetooth_utils::list_characteristics(self.session.get_connection(), &self.object_path) } } diff --git a/src/bluetooth_obex.rs b/src/bluetooth_obex.rs index 9e099f7..d6572b9 100644 --- a/src/bluetooth_obex.rs +++ b/src/bluetooth_obex.rs @@ -9,6 +9,7 @@ use std::thread::sleep; use std::time::Duration; use bluetooth_device::BluetoothDevice; +use bluetooth_session::BluetoothSession; const OBEX_BUS: &str = "org.bluez.obex"; const OBEX_PATH: &str = "/org/bluez/obex"; @@ -61,18 +62,17 @@ pub fn open_bus_connection() -> Result> { Ok(c) } -#[derive(Debug)] -pub struct BluetoothOBEXSession { - connection: Connection, +pub struct BluetoothOBEXSession<'a> { + session: &'a BluetoothSession, object_path: String, } -impl BluetoothOBEXSession { +impl<'a> BluetoothOBEXSession<'a> { // https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/obex-api.txt#n12 pub fn new( - connection: Connection, + session: &'a BluetoothSession, device: &BluetoothDevice, - ) -> Result> { + ) -> Result, Box> { let device_address: String = device.get_address()?; let mut map = HashMap::new(); map.insert("Target", Variant(SessionTarget::Opp.as_str())); @@ -80,11 +80,11 @@ impl BluetoothOBEXSession { let m = Message::new_method_call(OBEX_BUS, OBEX_PATH, CLIENT_INTERFACE, "CreateSession")? .append2(device_address, args); - let r = connection.send_with_reply_and_block(m, 1000)?; + let r = session.get_connection().send_with_reply_and_block(m, 1000)?; let session_path: ObjectPath = r.read1()?; let session_str: String = session_path.parse()?; let obex_session = BluetoothOBEXSession { - connection, + session, object_path: session_str, }; Ok(obex_session) @@ -95,14 +95,16 @@ impl BluetoothOBEXSession { let object_path = ObjectPath::new(self.object_path.as_bytes())?; let m = Message::new_method_call(OBEX_BUS, OBEX_PATH, CLIENT_INTERFACE, "RemoveSession")? .append1(object_path); - let _r = self.connection.send_with_reply_and_block(m, 1000)?; + let _r = self + .session + .get_connection() + .send_with_reply_and_block(m, 1000)?; Ok(()) } } -#[derive(Debug)] pub struct BluetoothOBEXTransfer<'a> { - session: &'a BluetoothOBEXSession, + session: &'a BluetoothOBEXSession<'a>, object_path: String, name: String, } @@ -114,10 +116,12 @@ impl<'a> BluetoothOBEXTransfer<'a> { file_path: &str, ) -> Result, Box> { let session_path: String = session.object_path.clone(); - let m = - Message::new_method_call(OBEX_BUS, session_path, OBJECT_PUSH_INTERFACE, "SendFile")? - .append1(file_path); - let r = session.connection.send_with_reply_and_block(m, 1000)?; + let m = Message::new_method_call(OBEX_BUS, session_path, OBJECT_PUSH_INTERFACE, "SendFile")? + .append1(file_path); + let r = session + .session + .get_connection() + .send_with_reply_and_block(m, 1000)?; let transfer_path: ObjectPath = r.read1()?; let transfer_str: String = transfer_path.parse()?; @@ -138,7 +142,7 @@ impl<'a> BluetoothOBEXTransfer<'a> { pub fn status(&self) -> Result> { let transfer_path = self.object_path.clone(); let p = Props::new( - &self.session.connection, + &self.session.session.get_connection(), OBEX_BUS, transfer_path, TRANSFER_INTERFACE, diff --git a/src/bluetooth_session.rs b/src/bluetooth_session.rs new file mode 100644 index 0000000..d9a5481 --- /dev/null +++ b/src/bluetooth_session.rs @@ -0,0 +1,120 @@ +use dbus::{arg::cast, arg::RefArg, arg::TypeMismatchError, arg::Variant, BusType, Connection}; +use std::collections::HashMap; +use std::error::Error; + +use bluetooth_adapter::BluetoothAdapter; +use bluetooth_device::BluetoothDevice; + +use std::sync::mpsc; + +static BLUEZ_MATCH: &'static str = "sender='org.bluez'"; + +pub struct BluetoothSession { + connection: Connection, + powered_callback: Option>, + discovering_callback: Option>, + connected_callback: Option>, + services_resolved_callback: Option>, +} + +impl BluetoothSession { + pub fn create_session() -> Result> { + let c = try!(Connection::get_private(BusType::System)); + Ok(BluetoothSession::new(c)) + } + + fn new(connection: Connection) -> BluetoothSession { + BluetoothSession { + connection: connection, + powered_callback: None, + discovering_callback: None, + connected_callback: None, + services_resolved_callback: None, + } + } + + pub fn get_connection(&self) -> &Connection { + &self.connection + } + + pub fn set_powered_callback(&mut self, callback: Box) { + self.powered_callback = Some(callback); + } + + pub fn set_discovering_callback(&mut self, callback: Box) { + self.discovering_callback = Some(callback); + } + + pub fn set_connected_callback(&mut self, callback: Box) { + self.connected_callback = Some(callback); + } + + pub fn set_services_resolved_callback(&mut self, callback: Box) { + self.services_resolved_callback = Some(callback); + } + + pub fn listen(&mut self, terminate: mpsc::Receiver) -> Result<(), Box> { + self.connection.add_match(BLUEZ_MATCH)?; + loop { + for conn_msg in self.connection.incoming(1000) { + let result: Result< + (&str, HashMap>>), + TypeMismatchError, + > = conn_msg.read2(); + + match result { + Ok((_, properties)) => { + let object_path = conn_msg.path().unwrap().to_string(); + + if let Some(value) = properties.get("Powered") { + if let Some(powered) = cast::(&value.0) { + if let Some(ref callback) = self.powered_callback { + callback( + BluetoothAdapter::create_adapter(self, object_path.clone()) + .unwrap(), + powered, + ); + } + } + } + + if let Some(value) = properties.get("Discovering") { + if let Some(discovering) = cast::(&value.0) { + if let Some(ref callback) = self.discovering_callback { + callback( + BluetoothAdapter::create_adapter(self, object_path.clone()) + .unwrap(), + discovering, + ); + } + } + } + + if let Some(value) = properties.get("Connected") { + if let Some(c) = cast::(&value.0) { + if let Some(ref callback) = self.connected_callback { + callback(BluetoothDevice::new(self, object_path.clone()), c); + } + } + } + + if let Some(value) = properties.get("ServicesResolved") { + if let Some(s) = cast::(&value.0) { + println!("{} services_resolved={}", conn_msg.path().unwrap(), s); + } + } + } + _ => {} + } + } + + let t = terminate.try_recv(); + match t { + Ok(true) => { + return Ok(()); + } + _ => {} + } + } + } +} diff --git a/src/bluetooth_utils.rs b/src/bluetooth_utils.rs index dd320bb..bfac40d 100644 --- a/src/bluetooth_utils.rs +++ b/src/bluetooth_utils.rs @@ -1,4 +1,4 @@ -use dbus::{Connection, BusType, Message, MessageItem, Props}; +use dbus::{Connection, Message, MessageItem, Props}; use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; @@ -8,22 +8,26 @@ static CHARACTERISTIC_INTERFACE: &'static str = "org.bluez.GattCharacteristic1"; static DESCRIPTOR_INTERFACE: &'static str = "org.bluez.GattDescriptor1"; static SERVICE_NAME: &'static str = "org.bluez"; -fn get_managed_objects(c: &Connection) -> Result, Box> { - let m = try!(Message::new_method_call(SERVICE_NAME, "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")); +fn get_managed_objects(c: &Connection) -> Result, Box> { + let m = try!(Message::new_method_call( + SERVICE_NAME, + "/", + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects" + )); let r = try!(c.send_with_reply_and_block(m, 1000)); Ok(r.get_items()) } -pub fn get_adapters() -> Result, Box> { +pub fn get_adapters(c: &Connection) -> Result, Box> { let mut adapters: Vec = Vec::new(); - let c = try!(Connection::get_private(BusType::System)); let objects: Vec = try!(get_managed_objects(&c)); let z: &[MessageItem] = objects.get(0).unwrap().inner().unwrap(); for y in z { let (path, interfaces) = y.inner().unwrap(); let x: &[MessageItem] = interfaces.inner().unwrap(); for interface in x { - let (i,_) = interface.inner().unwrap(); + let (i, _) = interface.inner().unwrap(); let name: &str = i.inner().unwrap(); if name == ADAPTER_INTERFACE { let p: &str = path.inner().unwrap(); @@ -34,36 +38,43 @@ pub fn get_adapters() -> Result, Box> { Ok(adapters) } -pub fn list_devices(adapter_path: &String) -> Result, Box> { - list_item(DEVICE_INTERFACE, adapter_path, "Adapter") +pub fn list_devices(c: &Connection, adapter_path: &String) -> Result, Box> { + list_item(c, DEVICE_INTERFACE, adapter_path, "Adapter") } -pub fn list_services(device_path: &String) -> Result, Box> { - list_item(SERVICE_INTERFACE, device_path, "Device") +pub fn list_services(c: &Connection, device_path: &String) -> Result, Box> { + list_item(c, SERVICE_INTERFACE, device_path, "Device") } -pub fn list_characteristics(device_path: &String) -> Result, Box> { - list_item(CHARACTERISTIC_INTERFACE, device_path, "Service") +pub fn list_characteristics( + c: &Connection, + device_path: &String, +) -> Result, Box> { + list_item(c, CHARACTERISTIC_INTERFACE, device_path, "Service") } -pub fn list_descriptors(device_path: &String) -> Result, Box> { - list_item(DESCRIPTOR_INTERFACE, device_path, "Characteristic") +pub fn list_descriptors(c: &Connection, device_path: &String) -> Result, Box> { + list_item(c, DESCRIPTOR_INTERFACE, device_path, "Characteristic") } -fn list_item(item_interface: &str, item_path: &str, item_property: &str) -> Result, Box> { +fn list_item( + c: &Connection, + item_interface: &str, + item_path: &str, + item_property: &str, +) -> Result, Box> { let mut v: Vec = Vec::new(); - let c = try!(Connection::get_private(BusType::System)); let objects: Vec = try!(get_managed_objects(&c)); let z: &[MessageItem] = objects.get(0).unwrap().inner().unwrap(); for y in z { let (path, interfaces) = y.inner().unwrap(); let x: &[MessageItem] = interfaces.inner().unwrap(); for interface in x { - let (i,_) = interface.inner().unwrap(); + let (i, _) = interface.inner().unwrap(); let name: &str = i.inner().unwrap(); if name == item_interface { let objpath: &str = path.inner().unwrap(); - let prop = try!(get_property(item_interface, objpath, item_property)); + let prop = try!(get_property(c, item_interface, objpath, item_property)); let prop_path = prop.inner::<&str>().unwrap(); if prop_path == item_path { v.push(String::from(objpath)); @@ -74,22 +85,43 @@ fn list_item(item_interface: &str, item_path: &str, item_property: &str) -> Resu Ok(v) } -pub fn get_property(interface: &str, object_path: &str, prop: &str) -> Result> { - let c = try!(Connection::get_private(BusType::System)); +pub fn get_property( + c: &Connection, + interface: &str, + object_path: &str, + prop: &str, +) -> Result> { let p = Props::new(&c, SERVICE_NAME, object_path, interface, 1000); Ok(try!(p.get(prop)).clone()) } -pub fn set_property(interface: &str, object_path: &str, prop: &str, value: T) -> Result<(), Box> -where T: Into { - let c = try!(Connection::get_private(BusType::System)); +pub fn set_property( + c: &Connection, + interface: &str, + object_path: &str, + prop: &str, + value: T, +) -> Result<(), Box> +where + T: Into, +{ let p = Props::new(&c, SERVICE_NAME, object_path, interface, 1000); Ok(try!(p.set(prop, value.into()))) } -pub fn call_method(interface: &str, object_path: &str, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { - let c = try!(Connection::get_private(BusType::System)); - let mut m = try!(Message::new_method_call(SERVICE_NAME, object_path, interface, method)); +pub fn call_method( + c: &Connection, + interface: &str, + object_path: &str, + method: &str, + param: Option<&[MessageItem]>, +) -> Result<(), Box> { + let mut m = try!(Message::new_method_call( + SERVICE_NAME, + object_path, + interface, + method + )); match param { Some(p) => m.append_items(p), None => (), diff --git a/src/lib.rs b/src/lib.rs index b8d7598..c865de4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ extern crate dbus; extern crate hex; +pub use bluetooth_session::BluetoothSession; pub use bluetooth_adapter::BluetoothAdapter; pub use bluetooth_device::BluetoothDevice; pub use bluetooth_gatt_characteristic::BluetoothGATTCharacteristic; @@ -9,6 +10,7 @@ pub use bluetooth_gatt_service::BluetoothGATTService; pub use bluetooth_discovery_session::BluetoothDiscoverySession; pub use bluetooth_obex::BluetoothOBEXSession; +pub mod bluetooth_session; pub mod bluetooth_device; pub mod bluetooth_adapter; pub mod bluetooth_gatt_characteristic; From 6fbfc7228ebdc136ac580b34a137652c6f03c933 Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Tue, 11 Sep 2018 22:42:31 +0300 Subject: [PATCH 02/12] added path filter to BluetoothSession BluetoothDiscoverySession now uses a connection from BluetoothSession filter only signals --- src/bluetooth_discovery_session.rs | 32 +++-- src/bluetooth_session.rs | 197 +++++++++++++++-------------- 2 files changed, 124 insertions(+), 105 deletions(-) diff --git a/src/bluetooth_discovery_session.rs b/src/bluetooth_discovery_session.rs index 2d23e9e..1e6d951 100644 --- a/src/bluetooth_discovery_session.rs +++ b/src/bluetooth_discovery_session.rs @@ -1,34 +1,46 @@ +use bluetooth_session::BluetoothSession; use dbus::{BusType, Connection, Message, MessageItem}; use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; static SERVICE_NAME: &'static str = "org.bluez"; -pub struct BluetoothDiscoverySession { +pub struct BluetoothDiscoverySession<'a> { adapter: String, - connection: Connection, + session: &'a BluetoothSession, } -impl BluetoothDiscoverySession { - pub fn create_session(adapter: String) -> Result> { - let c = try!(Connection::get_private(BusType::System)); - Ok(BluetoothDiscoverySession::new(adapter, c)) +impl<'a> BluetoothDiscoverySession<'a> { + pub fn create_session( + session: &'a BluetoothSession, + adapter: String, + ) -> Result> { + Ok(BluetoothDiscoverySession::new(session, adapter)) } - fn new(adapter: String, connection: Connection) -> BluetoothDiscoverySession { + fn new(session: &'a BluetoothSession, adapter: String) -> BluetoothDiscoverySession<'a> { BluetoothDiscoverySession { adapter: adapter, - connection: connection, + session: session, } } fn call_method(&self, method: &str, param: Option<[MessageItem; 1]>) -> Result<(), Box> { - let mut m = try!(Message::new_method_call(SERVICE_NAME, &self.adapter, ADAPTER_INTERFACE, method)); + let mut m = try!(Message::new_method_call( + SERVICE_NAME, + &self.adapter, + ADAPTER_INTERFACE, + method + )); match param { Some(p) => m.append_items(&p), None => (), }; - try!(self.connection.send_with_reply_and_block(m, 1000)); + try!( + self.session + .get_connection() + .send_with_reply_and_block(m, 1000) + ); Ok(()) } diff --git a/src/bluetooth_session.rs b/src/bluetooth_session.rs index d9a5481..8722334 100644 --- a/src/bluetooth_session.rs +++ b/src/bluetooth_session.rs @@ -1,35 +1,117 @@ -use dbus::{arg::cast, arg::RefArg, arg::TypeMismatchError, arg::Variant, BusType, Connection}; +use dbus::{ + arg::cast, arg::RefArg, arg::TypeMismatchError, arg::Variant, BusType, ConnMsgs, Connection, + Message, +}; use std::collections::HashMap; use std::error::Error; -use bluetooth_adapter::BluetoothAdapter; -use bluetooth_device::BluetoothDevice; - -use std::sync::mpsc; - -static BLUEZ_MATCH: &'static str = "sender='org.bluez'"; +static BLUEZ_MATCH: &'static str = "type='signal',sender='org.bluez'"; pub struct BluetoothSession { connection: Connection, - powered_callback: Option>, - discovering_callback: Option>, - connected_callback: Option>, - services_resolved_callback: Option>, +} + +#[derive(Clone)] +pub enum BluetoothEvent { + Powered { + object_path: String, + powered: bool, + }, + Discovering { + object_path: String, + discovering: bool, + }, + Connected { + object_path: String, + connected: bool, + }, + ServicesResolved { + object_path: String, + services_resolved: bool, + }, + None, +} + +impl BluetoothEvent { + pub fn from(conn_msg: Message) -> Option { + let result: Result< + (&str, HashMap>>), + TypeMismatchError, + > = conn_msg.read2(); + + match result { + Ok((_, properties)) => { + let object_path = conn_msg.path().unwrap().to_string(); + + if let Some(value) = properties.get("Powered") { + if let Some(powered) = cast::(&value.0) { + let event = BluetoothEvent::Powered { + object_path: object_path.clone(), + powered: *powered, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("Discovering") { + if let Some(discovering) = cast::(&value.0) { + let event = BluetoothEvent::Discovering { + object_path: object_path.clone(), + discovering: *discovering, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("Connected") { + if let Some(connected) = cast::(&value.0) { + let event = BluetoothEvent::Connected { + object_path: object_path.clone(), + connected: *connected, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("ServicesResolved") { + if let Some(services_resolved) = cast::(&value.0) { + let event = BluetoothEvent::ServicesResolved { + object_path: object_path.clone(), + services_resolved: *services_resolved, + }; + + return Some(event); + } + } + + Some(BluetoothEvent::None) + } + Err(err) => None, + } + } } impl BluetoothSession { - pub fn create_session() -> Result> { + pub fn create_session(path: Option<&str>) -> Result> { + let mut rule = { + if let Some(path) = path { + format!("{},path='{}'", BLUEZ_MATCH, path) + } else { + String::from(BLUEZ_MATCH) + } + }; + let c = try!(Connection::get_private(BusType::System)); + c.add_match(rule.as_str())?; Ok(BluetoothSession::new(c)) } fn new(connection: Connection) -> BluetoothSession { BluetoothSession { connection: connection, - powered_callback: None, - discovering_callback: None, - connected_callback: None, - services_resolved_callback: None, } } @@ -37,84 +119,9 @@ impl BluetoothSession { &self.connection } - pub fn set_powered_callback(&mut self, callback: Box) { - self.powered_callback = Some(callback); - } - - pub fn set_discovering_callback(&mut self, callback: Box) { - self.discovering_callback = Some(callback); - } - - pub fn set_connected_callback(&mut self, callback: Box) { - self.connected_callback = Some(callback); - } - - pub fn set_services_resolved_callback(&mut self, callback: Box) { - self.services_resolved_callback = Some(callback); - } - - pub fn listen(&mut self, terminate: mpsc::Receiver) -> Result<(), Box> { - self.connection.add_match(BLUEZ_MATCH)?; - loop { - for conn_msg in self.connection.incoming(1000) { - let result: Result< - (&str, HashMap>>), - TypeMismatchError, - > = conn_msg.read2(); - - match result { - Ok((_, properties)) => { - let object_path = conn_msg.path().unwrap().to_string(); - - if let Some(value) = properties.get("Powered") { - if let Some(powered) = cast::(&value.0) { - if let Some(ref callback) = self.powered_callback { - callback( - BluetoothAdapter::create_adapter(self, object_path.clone()) - .unwrap(), - powered, - ); - } - } - } - - if let Some(value) = properties.get("Discovering") { - if let Some(discovering) = cast::(&value.0) { - if let Some(ref callback) = self.discovering_callback { - callback( - BluetoothAdapter::create_adapter(self, object_path.clone()) - .unwrap(), - discovering, - ); - } - } - } - - if let Some(value) = properties.get("Connected") { - if let Some(c) = cast::(&value.0) { - if let Some(ref callback) = self.connected_callback { - callback(BluetoothDevice::new(self, object_path.clone()), c); - } - } - } - - if let Some(value) = properties.get("ServicesResolved") { - if let Some(s) = cast::(&value.0) { - println!("{} services_resolved={}", conn_msg.path().unwrap(), s); - } - } - } - _ => {} - } - } - - let t = terminate.try_recv(); - match t { - Ok(true) => { - return Ok(()); - } - _ => {} - } - } + pub fn incoming(&self, timeout_ms: u32) -> ConnMsgs<&Connection> { + self.connection.incoming(timeout_ms) } } + +unsafe impl Send for BluetoothEvent {} From ca4cfc8be8fcf03a9b918a50fecd177c3f24ca82 Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Thu, 13 Sep 2018 10:31:19 +0300 Subject: [PATCH 03/12] added value change BluetoothEvent moved BluetoothEvent to a separate module fixed examples/ --- examples/test.rs | 4 +- examples/test2.rs | 9 ++- examples/test3.rs | 7 +- examples/test4.rs | 5 +- examples/test5.rs | 22 ++++++ src/bluetooth_device.rs | 2 +- src/bluetooth_event.rs | 100 +++++++++++++++++++++++++++ src/bluetooth_gatt_characteristic.rs | 2 +- src/bluetooth_gatt_descriptor.rs | 2 +- src/bluetooth_gatt_service.rs | 2 +- src/bluetooth_session.rs | 93 +------------------------ src/lib.rs | 12 ++-- 12 files changed, 151 insertions(+), 109 deletions(-) create mode 100644 examples/test5.rs create mode 100644 src/bluetooth_event.rs diff --git a/examples/test.rs b/examples/test.rs index 696d456..9e32344 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -2,12 +2,12 @@ extern crate blurz; use std::error::Error; -use blurz::bluetooth_session::BluetoothSession as Session; use blurz::bluetooth_adapter::BluetoothAdapter as Adapter; use blurz::bluetooth_device::BluetoothDevice as Device; +use blurz::bluetooth_session::BluetoothSession as Session; fn test() -> Result<(), Box> { - let session = &Session::create_session().unwrap(); + let session = &Session::create_session(None).unwrap(); let adapter: Adapter = try!(Adapter::init(session)); let device: Device = try!(adapter.get_first_device()); println!("{:?}", device); diff --git a/examples/test2.rs b/examples/test2.rs index 8fdd144..1440bd8 100644 --- a/examples/test2.rs +++ b/examples/test2.rs @@ -16,9 +16,12 @@ use blurz::bluetooth_gatt_service::BluetoothGATTService as Service; use blurz::bluetooth_session::BluetoothSession as Session; fn test2() -> Result<(), Box> { - let bt_session = &Session::create_session()?; + let bt_session = &Session::create_session(None)?; let adapter: Adapter = try!(Adapter::init(bt_session)); - let session = try!(DiscoverySession::create_session(adapter.get_id())); + let session = try!(DiscoverySession::create_session( + &bt_session, + adapter.get_id() + )); try!(session.start_discovery()); //let mut devices = vec!(); for _ in 0..5 { @@ -44,7 +47,7 @@ fn test2() -> Result<(), Box> { if uuid == COLOR_PICKER_SERVICE_UUID || uuid == BATTERY_SERVICE_UUID { println!("{:?} has a service!", device.get_alias()); println!("connect device..."); - device.connect().ok(); + device.connect(10000).ok(); if try!(device.is_connected()) { println!("checking gatt..."); // We need to wait a bit after calling connect to safely diff --git a/examples/test3.rs b/examples/test3.rs index adb463d..37df023 100644 --- a/examples/test3.rs +++ b/examples/test3.rs @@ -10,11 +10,14 @@ use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as DiscoverySe use blurz::bluetooth_session::BluetoothSession as Session; fn test3() -> Result<(), Box> { - let bt_session = &Session::create_session()?; + let bt_session = &Session::create_session(None)?; let adapter: Adapter = try!(Adapter::init(bt_session)); try!(adapter.set_powered(true)); loop { - let session = try!(DiscoverySession::create_session(adapter.get_id())); + let session = try!(DiscoverySession::create_session( + &bt_session, + adapter.get_id() + )); thread::sleep(Duration::from_millis(200)); try!(session.start_discovery()); thread::sleep(Duration::from_millis(800)); diff --git a/examples/test4.rs b/examples/test4.rs index 6f005e2..9bef433 100644 --- a/examples/test4.rs +++ b/examples/test4.rs @@ -14,7 +14,7 @@ use blurz::bluetooth_obex::{ use blurz::bluetooth_session::BluetoothSession as Session; fn test_obex_file_transfer() -> Result<(), Box> { - let session = &Session::create_session()?; + let session = &Session::create_session(None)?; let adapter: Adapter = Adapter::init(session)?; let devices: Vec = adapter.get_device_list()?; @@ -23,8 +23,7 @@ fn test_obex_file_transfer() -> Result<(), Box> { .filter(|&device_id| { let device = Device::new(session, device_id.to_string()); device.is_ready_to_receive().unwrap() - }) - .cloned() + }).cloned() .collect::>(); let device_id: &str = &filtered_devices[0]; diff --git a/examples/test5.rs b/examples/test5.rs new file mode 100644 index 0000000..7ecacd9 --- /dev/null +++ b/examples/test5.rs @@ -0,0 +1,22 @@ +extern crate blurz; + +use std::error::Error; + +use blurz::bluetooth_event::BluetoothEvent; +use blurz::bluetooth_session::BluetoothSession as Session; + +fn test5() -> Result<(), Box> { + let session = &Session::create_session(Some("/org/bluez/hci0")).unwrap(); + loop { + for event in session.incoming(1000).map(BluetoothEvent::from) { + println!("{:?}", event); + } + } +} + +fn main() { + match test5() { + Ok(_) => (), + Err(e) => println!("{:?}", e), + } +} diff --git a/src/bluetooth_device.rs b/src/bluetooth_device.rs index 11aad23..de14a37 100644 --- a/src/bluetooth_device.rs +++ b/src/bluetooth_device.rs @@ -7,7 +7,7 @@ use std::error::Error; static DEVICE_INTERFACE: &'static str = "org.bluez.Device1"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BluetoothDevice<'a> { object_path: String, session: &'a BluetoothSession, diff --git a/src/bluetooth_event.rs b/src/bluetooth_event.rs new file mode 100644 index 0000000..0a03b86 --- /dev/null +++ b/src/bluetooth_event.rs @@ -0,0 +1,100 @@ +use dbus::{arg::cast, arg::RefArg, arg::TypeMismatchError, arg::Variant, Message}; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub enum BluetoothEvent { + Powered { + object_path: String, + powered: bool, + }, + Discovering { + object_path: String, + discovering: bool, + }, + Connected { + object_path: String, + connected: bool, + }, + ServicesResolved { + object_path: String, + services_resolved: bool, + }, + Value { + object_path: String, + value: Box<[u8]>, + }, + None, +} + +impl BluetoothEvent { + pub fn from(conn_msg: Message) -> Option { + let result: Result< + (&str, HashMap>>), + TypeMismatchError, + > = conn_msg.read2(); + + match result { + Ok((_, properties)) => { + let object_path = conn_msg.path().unwrap().to_string(); + + if let Some(value) = properties.get("Powered") { + if let Some(powered) = cast::(&value.0) { + let event = BluetoothEvent::Powered { + object_path: object_path.clone(), + powered: *powered, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("Discovering") { + if let Some(discovering) = cast::(&value.0) { + let event = BluetoothEvent::Discovering { + object_path: object_path.clone(), + discovering: *discovering, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("Connected") { + if let Some(connected) = cast::(&value.0) { + let event = BluetoothEvent::Connected { + object_path: object_path.clone(), + connected: *connected, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("ServicesResolved") { + if let Some(services_resolved) = cast::(&value.0) { + let event = BluetoothEvent::ServicesResolved { + object_path: object_path.clone(), + services_resolved: *services_resolved, + }; + + return Some(event); + } + } + + if let Some(value) = properties.get("Value") { + if let Some(value) = cast::>(&value.0) { + let event = BluetoothEvent::Value { + object_path: object_path.clone(), + value: value.clone().into_boxed_slice(), + }; + + return Some(event); + } + } + + Some(BluetoothEvent::None) + } + Err(_err) => None, + } + } +} diff --git a/src/bluetooth_gatt_characteristic.rs b/src/bluetooth_gatt_characteristic.rs index 57f6966..8022b0c 100644 --- a/src/bluetooth_gatt_characteristic.rs +++ b/src/bluetooth_gatt_characteristic.rs @@ -7,7 +7,7 @@ use std::error::Error; static SERVICE_NAME: &'static str = "org.bluez"; static GATT_CHARACTERISTIC_INTERFACE: &'static str = "org.bluez.GattCharacteristic1"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BluetoothGATTCharacteristic<'a> { object_path: String, session: &'a BluetoothSession, diff --git a/src/bluetooth_gatt_descriptor.rs b/src/bluetooth_gatt_descriptor.rs index bc64a4e..286edb6 100644 --- a/src/bluetooth_gatt_descriptor.rs +++ b/src/bluetooth_gatt_descriptor.rs @@ -7,7 +7,7 @@ use std::error::Error; static SERVICE_NAME: &'static str = "org.bluez"; static GATT_DESCRIPTOR_INTERFACE: &'static str = "org.bluez.GattDescriptor1"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BluetoothGATTDescriptor<'a> { object_path: String, session: &'a BluetoothSession, diff --git a/src/bluetooth_gatt_service.rs b/src/bluetooth_gatt_service.rs index e076522..4ee28b5 100644 --- a/src/bluetooth_gatt_service.rs +++ b/src/bluetooth_gatt_service.rs @@ -6,7 +6,7 @@ use std::error::Error; static GATT_SERVICE_INTERFACE: &'static str = "org.bluez.GattService1"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BluetoothGATTService<'a> { object_path: String, session: &'a BluetoothSession, diff --git a/src/bluetooth_session.rs b/src/bluetooth_session.rs index 8722334..83c9171 100644 --- a/src/bluetooth_session.rs +++ b/src/bluetooth_session.rs @@ -1,99 +1,14 @@ -use dbus::{ - arg::cast, arg::RefArg, arg::TypeMismatchError, arg::Variant, BusType, ConnMsgs, Connection, - Message, -}; -use std::collections::HashMap; +use dbus::{BusType, ConnMsgs, Connection}; + use std::error::Error; static BLUEZ_MATCH: &'static str = "type='signal',sender='org.bluez'"; +#[derive(Debug)] pub struct BluetoothSession { connection: Connection, } -#[derive(Clone)] -pub enum BluetoothEvent { - Powered { - object_path: String, - powered: bool, - }, - Discovering { - object_path: String, - discovering: bool, - }, - Connected { - object_path: String, - connected: bool, - }, - ServicesResolved { - object_path: String, - services_resolved: bool, - }, - None, -} - -impl BluetoothEvent { - pub fn from(conn_msg: Message) -> Option { - let result: Result< - (&str, HashMap>>), - TypeMismatchError, - > = conn_msg.read2(); - - match result { - Ok((_, properties)) => { - let object_path = conn_msg.path().unwrap().to_string(); - - if let Some(value) = properties.get("Powered") { - if let Some(powered) = cast::(&value.0) { - let event = BluetoothEvent::Powered { - object_path: object_path.clone(), - powered: *powered, - }; - - return Some(event); - } - } - - if let Some(value) = properties.get("Discovering") { - if let Some(discovering) = cast::(&value.0) { - let event = BluetoothEvent::Discovering { - object_path: object_path.clone(), - discovering: *discovering, - }; - - return Some(event); - } - } - - if let Some(value) = properties.get("Connected") { - if let Some(connected) = cast::(&value.0) { - let event = BluetoothEvent::Connected { - object_path: object_path.clone(), - connected: *connected, - }; - - return Some(event); - } - } - - if let Some(value) = properties.get("ServicesResolved") { - if let Some(services_resolved) = cast::(&value.0) { - let event = BluetoothEvent::ServicesResolved { - object_path: object_path.clone(), - services_resolved: *services_resolved, - }; - - return Some(event); - } - } - - Some(BluetoothEvent::None) - } - Err(err) => None, - } - } -} - impl BluetoothSession { pub fn create_session(path: Option<&str>) -> Result> { let mut rule = { @@ -123,5 +38,3 @@ impl BluetoothSession { self.connection.incoming(timeout_ms) } } - -unsafe impl Send for BluetoothEvent {} diff --git a/src/lib.rs b/src/lib.rs index c865de4..44164f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,23 @@ extern crate dbus; extern crate hex; -pub use bluetooth_session::BluetoothSession; pub use bluetooth_adapter::BluetoothAdapter; pub use bluetooth_device::BluetoothDevice; +pub use bluetooth_discovery_session::BluetoothDiscoverySession; +pub use bluetooth_event::BluetoothEvent; pub use bluetooth_gatt_characteristic::BluetoothGATTCharacteristic; pub use bluetooth_gatt_descriptor::BluetoothGATTDescriptor; pub use bluetooth_gatt_service::BluetoothGATTService; -pub use bluetooth_discovery_session::BluetoothDiscoverySession; pub use bluetooth_obex::BluetoothOBEXSession; +pub use bluetooth_session::BluetoothSession; -pub mod bluetooth_session; -pub mod bluetooth_device; pub mod bluetooth_adapter; +pub mod bluetooth_device; +pub mod bluetooth_discovery_session; +pub mod bluetooth_event; pub mod bluetooth_gatt_characteristic; pub mod bluetooth_gatt_descriptor; pub mod bluetooth_gatt_service; -pub mod bluetooth_discovery_session; pub mod bluetooth_obex; +pub mod bluetooth_session; mod bluetooth_utils; From 68465c8fbe330f6c66e481b4152f71df7ecc2fec Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Wed, 19 Sep 2018 00:59:15 +0300 Subject: [PATCH 04/12] implemented acquire_write and acquire_notify --- src/bluetooth_gatt_characteristic.rs | 38 +++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/bluetooth_gatt_characteristic.rs b/src/bluetooth_gatt_characteristic.rs index 8022b0c..ced8a7e 100644 --- a/src/bluetooth_gatt_characteristic.rs +++ b/src/bluetooth_gatt_characteristic.rs @@ -1,6 +1,6 @@ use bluetooth_session::BluetoothSession; use bluetooth_utils; -use dbus::{BusType, Connection, Message, MessageItem, MessageItemArray, Signature}; +use dbus::{BusType, Connection, Message, MessageItem, MessageItemArray, OwnedFd, Signature}; use std::error::Error; @@ -166,4 +166,40 @@ impl<'a> BluetoothGATTCharacteristic<'a> { pub fn stop_notify(&self) -> Result<(), Box> { self.call_method("StopNotify", None) } + + pub fn acquire_notify(&self) -> Result<(OwnedFd, u16), Box> { + let mut m = Message::new_method_call( + SERVICE_NAME, + &self.object_path, + GATT_CHARACTERISTIC_INTERFACE, + "AcquireNotify", + )?; + m.append_items(&[MessageItem::Array( + MessageItemArray::new(vec![], Signature::from("a{sv}")).unwrap(), + )]); + let reply = self + .session + .get_connection() + .send_with_reply_and_block(m, 1000)?; + let (opt_fd, opt_mtu) = reply.get2::(); + Ok((opt_fd.unwrap(), opt_mtu.unwrap())) + } + + pub fn acquire_write(&self) -> Result<(OwnedFd, u16), Box> { + let mut m = Message::new_method_call( + SERVICE_NAME, + &self.object_path, + GATT_CHARACTERISTIC_INTERFACE, + "AcquireWrite", + )?; + m.append_items(&[MessageItem::Array( + MessageItemArray::new(vec![], Signature::from("a{sv}")).unwrap(), + )]); + let reply = self + .session + .get_connection() + .send_with_reply_and_block(m, 1000)?; + let (opt_fd, opt_mtu) = reply.get2::(); + Ok((opt_fd.unwrap(), opt_mtu.unwrap())) + } } From 80223b775d34db1fa28293c063093265dc05ceba Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Thu, 27 Sep 2018 15:59:15 +0300 Subject: [PATCH 05/12] use different timeouts than 1000 ms use a longer timeout for Adapter::set_powered() increase the write_value timeout to 10 seconds --- src/bluetooth_adapter.rs | 24 ++++++++++++++++-------- src/bluetooth_device.rs | 27 +++++++++++++++++---------- src/bluetooth_gatt_characteristic.rs | 13 ++++++++++--- src/bluetooth_gatt_descriptor.rs | 9 ++++++++- src/bluetooth_utils.rs | 6 ++++-- 5 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/bluetooth_adapter.rs b/src/bluetooth_adapter.rs index 18cfdd9..3f3fa84 100644 --- a/src/bluetooth_adapter.rs +++ b/src/bluetooth_adapter.rs @@ -74,7 +74,7 @@ impl<'a> BluetoothAdapter<'a> { ) } - fn set_property(&self, prop: &str, value: T) -> Result<(), Box> + fn set_property(&self, prop: &str, value: T, timeout_ms: i32) -> Result<(), Box> where T: Into, { @@ -84,16 +84,23 @@ impl<'a> BluetoothAdapter<'a> { &self.object_path, prop, value, + timeout_ms, ) } - fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { + fn call_method( + &self, + method: &str, + param: Option<&[MessageItem]>, + timeout_ms: i32, + ) -> Result<(), Box> { bluetooth_utils::call_method( self.session.get_connection(), ADAPTER_INTERFACE, &self.object_path, method, param, + timeout_ms, ) } @@ -121,7 +128,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n120 pub fn set_alias(&self, value: String) -> Result<(), Box> { - self.set_property("Alias", value) + self.set_property("Alias", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n139 @@ -138,7 +145,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n147 pub fn set_powered(&self, value: bool) -> Result<(), Box> { - self.set_property("Powered", value) + self.set_property("Powered", value, 10000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n156 @@ -149,7 +156,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n156 pub fn set_discoverable(&self, value: bool) -> Result<(), Box> { - self.set_property("Discoverable", value) + self.set_property("Discoverable", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n176 @@ -160,7 +167,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n176 pub fn set_pairable(&self, value: bool) -> Result<(), Box> { - self.set_property("Pairable", value) + self.set_property("Pairable", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n187 @@ -171,7 +178,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n187 pub fn set_pairable_timeout(&self, value: u32) -> Result<(), Box> { - self.set_property("PairableTimeout", value) + self.set_property("PairableTimeout", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n196 @@ -182,7 +189,7 @@ impl<'a> BluetoothAdapter<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n196 pub fn set_discoverable_timeout(&self, value: u32) -> Result<(), Box> { - self.set_property("DiscoverableTimeout", value) + self.set_property("DiscoverableTimeout", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/adapter-api.txt#n205 @@ -260,6 +267,7 @@ impl<'a> BluetoothAdapter<'a> { self.call_method( "RemoveDevice", Some(&[MessageItem::ObjectPath(device.into())]), + 1000, ) } } diff --git a/src/bluetooth_device.rs b/src/bluetooth_device.rs index de14a37..6198108 100644 --- a/src/bluetooth_device.rs +++ b/src/bluetooth_device.rs @@ -34,7 +34,7 @@ impl<'a> BluetoothDevice<'a> { ) } - fn set_property(&self, prop: &str, value: T) -> Result<(), Box> + fn set_property(&self, prop: &str, value: T, timeout_ms: i32) -> Result<(), Box> where T: Into, { @@ -44,16 +44,23 @@ impl<'a> BluetoothDevice<'a> { &self.object_path, prop, value, + timeout_ms, ) } - fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { + fn call_method( + &self, + method: &str, + param: Option<&[MessageItem]>, + timeout_ms: i32, + ) -> Result<(), Box> { bluetooth_utils::call_method( self.session.get_connection(), DEVICE_INTERFACE, &self.object_path, method, param, + timeout_ms, ) } @@ -127,7 +134,7 @@ impl<'a> BluetoothDevice<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n149 pub fn set_trusted(&self, value: bool) -> Result<(), Box> { - self.set_property("Trusted", value) + self.set_property("Trusted", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n149 @@ -150,7 +157,7 @@ impl<'a> BluetoothDevice<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n161 pub fn set_alias(&self, value: String) -> Result<(), Box> { - self.set_property("Alias", value) + self.set_property("Alias", value, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n174 @@ -269,31 +276,31 @@ impl<'a> BluetoothDevice<'a> { // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n12 pub fn connect(&self) -> Result<(), Box> { - self.call_method("Connect", None) + self.call_method("Connect", None, 30000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n29 pub fn disconnect(&self) -> Result<(), Box> { - self.call_method("Disconnect", None) + self.call_method("Disconnect", None, 5000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n43 pub fn connect_profile(&self, uuid: String) -> Result<(), Box> { - self.call_method("ConnectProfile", Some(&[uuid.into()])) + self.call_method("ConnectProfile", Some(&[uuid.into()]), 30000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n55 pub fn disconnect_profile(&self, uuid: String) -> Result<(), Box> { - self.call_method("DisconnectProfile", Some(&[uuid.into()])) + self.call_method("DisconnectProfile", Some(&[uuid.into()]), 5000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n70 pub fn pair(&self) -> Result<(), Box> { - self.call_method("Pair", None) + self.call_method("Pair", None, 60000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n97 pub fn cancel_pairing(&self) -> Result<(), Box> { - self.call_method("CancelPairing", None) + self.call_method("CancelPairing", None, 5000) } } diff --git a/src/bluetooth_gatt_characteristic.rs b/src/bluetooth_gatt_characteristic.rs index ced8a7e..b998106 100644 --- a/src/bluetooth_gatt_characteristic.rs +++ b/src/bluetooth_gatt_characteristic.rs @@ -34,13 +34,19 @@ impl<'a> BluetoothGATTCharacteristic<'a> { ) } - fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { + fn call_method( + &self, + method: &str, + param: Option<&[MessageItem]>, + timeout_ms: i32, + ) -> Result<(), Box> { bluetooth_utils::call_method( self.session.get_connection(), GATT_CHARACTERISTIC_INTERFACE, &self.object_path, method, param, + timeout_ms, ) } @@ -154,17 +160,18 @@ impl<'a> BluetoothGATTCharacteristic<'a> { ).unwrap(), ), ]), + 10000, ) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n96 pub fn start_notify(&self) -> Result<(), Box> { - self.call_method("StartNotify", None) + self.call_method("StartNotify", None, 1000) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/gatt-api.txt#n105 pub fn stop_notify(&self) -> Result<(), Box> { - self.call_method("StopNotify", None) + self.call_method("StopNotify", None, 1000) } pub fn acquire_notify(&self) -> Result<(OwnedFd, u16), Box> { diff --git a/src/bluetooth_gatt_descriptor.rs b/src/bluetooth_gatt_descriptor.rs index 286edb6..192d84d 100644 --- a/src/bluetooth_gatt_descriptor.rs +++ b/src/bluetooth_gatt_descriptor.rs @@ -34,13 +34,19 @@ impl<'a> BluetoothGATTDescriptor<'a> { ) } - fn call_method(&self, method: &str, param: Option<&[MessageItem]>) -> Result<(), Box> { + fn call_method( + &self, + method: &str, + param: Option<&[MessageItem]>, + timeout_ms: i32, + ) -> Result<(), Box> { bluetooth_utils::call_method( self.session.get_connection(), GATT_DESCRIPTOR_INTERFACE, &self.object_path, method, param, + timeout_ms, ) } @@ -143,6 +149,7 @@ impl<'a> BluetoothGATTDescriptor<'a> { ).unwrap(), ), ]), + 1000, ) } } diff --git a/src/bluetooth_utils.rs b/src/bluetooth_utils.rs index bfac40d..b16211b 100644 --- a/src/bluetooth_utils.rs +++ b/src/bluetooth_utils.rs @@ -101,11 +101,12 @@ pub fn set_property( object_path: &str, prop: &str, value: T, + timeout_ms: i32, ) -> Result<(), Box> where T: Into, { - let p = Props::new(&c, SERVICE_NAME, object_path, interface, 1000); + let p = Props::new(&c, SERVICE_NAME, object_path, interface, timeout_ms); Ok(try!(p.set(prop, value.into()))) } @@ -115,6 +116,7 @@ pub fn call_method( object_path: &str, method: &str, param: Option<&[MessageItem]>, + timeout_ms: i32, ) -> Result<(), Box> { let mut m = try!(Message::new_method_call( SERVICE_NAME, @@ -126,6 +128,6 @@ pub fn call_method( Some(p) => m.append_items(p), None => (), }; - try!(c.send_with_reply_and_block(m, 1000)); + try!(c.send_with_reply_and_block(m, timeout_ms)); Ok(()) } From aab1df7478071bf576786e31869d542aa6ec9d11 Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Sun, 14 Oct 2018 22:44:44 +0300 Subject: [PATCH 06/12] added timeout_ms to connect() signature --- src/bluetooth_device.rs | 4 ++-- src/bluetooth_discovery_session.rs | 2 +- src/bluetooth_obex.rs | 13 ++++++++----- src/bluetooth_session.rs | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/bluetooth_device.rs b/src/bluetooth_device.rs index 6198108..886402f 100644 --- a/src/bluetooth_device.rs +++ b/src/bluetooth_device.rs @@ -275,8 +275,8 @@ impl<'a> BluetoothDevice<'a> { */ // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n12 - pub fn connect(&self) -> Result<(), Box> { - self.call_method("Connect", None, 30000) + pub fn connect(&self, timeout_ms: i32) -> Result<(), Box> { + self.call_method("Connect", None, timeout_ms) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n29 diff --git a/src/bluetooth_discovery_session.rs b/src/bluetooth_discovery_session.rs index 1e6d951..450dba6 100644 --- a/src/bluetooth_discovery_session.rs +++ b/src/bluetooth_discovery_session.rs @@ -1,5 +1,5 @@ use bluetooth_session::BluetoothSession; -use dbus::{BusType, Connection, Message, MessageItem}; +use dbus::{Message, MessageItem}; use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; diff --git a/src/bluetooth_obex.rs b/src/bluetooth_obex.rs index d6572b9..8b0ec64 100644 --- a/src/bluetooth_obex.rs +++ b/src/bluetooth_obex.rs @@ -80,7 +80,9 @@ impl<'a> BluetoothOBEXSession<'a> { let m = Message::new_method_call(OBEX_BUS, OBEX_PATH, CLIENT_INTERFACE, "CreateSession")? .append2(device_address, args); - let r = session.get_connection().send_with_reply_and_block(m, 1000)?; + let r = session + .get_connection() + .send_with_reply_and_block(m, 1000)?; let session_path: ObjectPath = r.read1()?; let session_str: String = session_path.parse()?; let obex_session = BluetoothOBEXSession { @@ -106,7 +108,7 @@ impl<'a> BluetoothOBEXSession<'a> { pub struct BluetoothOBEXTransfer<'a> { session: &'a BluetoothOBEXSession<'a>, object_path: String, - name: String, + _name: String, } impl<'a> BluetoothOBEXTransfer<'a> { @@ -116,8 +118,9 @@ impl<'a> BluetoothOBEXTransfer<'a> { file_path: &str, ) -> Result, Box> { let session_path: String = session.object_path.clone(); - let m = Message::new_method_call(OBEX_BUS, session_path, OBJECT_PUSH_INTERFACE, "SendFile")? - .append1(file_path); + let m = + Message::new_method_call(OBEX_BUS, session_path, OBJECT_PUSH_INTERFACE, "SendFile")? + .append1(file_path); let r = session .session .get_connection() @@ -133,7 +136,7 @@ impl<'a> BluetoothOBEXTransfer<'a> { let obex_transfer = BluetoothOBEXTransfer { session, object_path: transfer_str, - name: file_name, + _name: file_name, }; Ok(obex_transfer) } diff --git a/src/bluetooth_session.rs b/src/bluetooth_session.rs index 83c9171..215ae65 100644 --- a/src/bluetooth_session.rs +++ b/src/bluetooth_session.rs @@ -11,7 +11,7 @@ pub struct BluetoothSession { impl BluetoothSession { pub fn create_session(path: Option<&str>) -> Result> { - let mut rule = { + let rule = { if let Some(path) = path { format!("{},path='{}'", BLUEZ_MATCH, path) } else { From 3e8eb0af14ccd7a4b998e106402c7d4cc529846b Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Tue, 23 Oct 2018 08:59:28 +0300 Subject: [PATCH 07/12] implemented BluetoothDiscoverySession::set_discovery_filter() --- src/bluetooth_discovery_session.rs | 45 +++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/bluetooth_discovery_session.rs b/src/bluetooth_discovery_session.rs index 450dba6..b4c9723 100644 --- a/src/bluetooth_discovery_session.rs +++ b/src/bluetooth_discovery_session.rs @@ -1,5 +1,5 @@ use bluetooth_session::BluetoothSession; -use dbus::{Message, MessageItem}; +use dbus::{Message, MessageItem, MessageItemArray, Signature}; use std::error::Error; static ADAPTER_INTERFACE: &'static str = "org.bluez.Adapter1"; @@ -51,4 +51,47 @@ impl<'a> BluetoothDiscoverySession<'a> { pub fn stop_discovery(&self) -> Result<(), Box> { self.call_method("StopDiscovery", None) } + + pub fn set_discovery_filter( + &self, + uuids: Vec, + rssi: Option, + pathloss: Option, + ) -> Result<(), Box> { + let uuids = { + let mut res: Vec = Vec::new(); + for u in uuids { + res.push(u.into()); + } + res + }; + + let mut m = vec![MessageItem::DictEntry( + Box::new("UUIDs".into()), + Box::new(MessageItem::Variant(Box::new( + MessageItem::new_array(uuids).unwrap(), + ))), + )]; + + if let Some(rssi) = rssi { + m.push(MessageItem::DictEntry( + Box::new("RSSI".into()), + Box::new(MessageItem::Variant(Box::new(rssi.into()))), + )) + } + + if let Some(pathloss) = pathloss { + m.push(MessageItem::DictEntry( + Box::new("Pathloss".into()), + Box::new(MessageItem::Variant(Box::new(pathloss.into()))), + )) + } + + self.call_method( + "SetDiscoveryFilter", + Some([MessageItem::Array( + MessageItemArray::new(m, Signature::from("a{sv}")).unwrap(), + )]), + ) + } } From c4829e6a4c2a9c715d73eb726da785a146792f4c Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Sun, 28 Oct 2018 09:35:53 +0200 Subject: [PATCH 08/12] added BluethootEvent::RSSI --- src/bluetooth_event.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bluetooth_event.rs b/src/bluetooth_event.rs index 0a03b86..a2e2846 100644 --- a/src/bluetooth_event.rs +++ b/src/bluetooth_event.rs @@ -23,6 +23,10 @@ pub enum BluetoothEvent { object_path: String, value: Box<[u8]>, }, + RSSI { + object_path: String, + rssi: i16, + }, None, } @@ -92,6 +96,17 @@ impl BluetoothEvent { } } + if let Some(value) = properties.get("RSSI") { + if let Some(rssi) = cast::(&value.0) { + let event = BluetoothEvent::RSSI { + object_path: object_path.clone(), + rssi: *rssi, + }; + + return Some(event); + } + } + Some(BluetoothEvent::None) } Err(_err) => None, From 7bac2e8313a76a9031b4a401f016db3c0ddf78b9 Mon Sep 17 00:00:00 2001 From: Gal Ben-Haim Date: Tue, 6 Nov 2018 15:27:28 +0200 Subject: [PATCH 09/12] implemented Adapter::connect_device() --- src/bluetooth_adapter.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/bluetooth_adapter.rs b/src/bluetooth_adapter.rs index 3f3fa84..12e3660 100644 --- a/src/bluetooth_adapter.rs +++ b/src/bluetooth_adapter.rs @@ -1,7 +1,7 @@ use bluetooth_device::BluetoothDevice; use bluetooth_session::BluetoothSession; use bluetooth_utils; -use dbus::MessageItem; +use dbus::{MessageItem, MessageItemArray, Signature}; use hex::FromHex; use std::error::Error; @@ -270,4 +270,40 @@ impl<'a> BluetoothAdapter<'a> { 1000, ) } + + // http://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt#n154 + pub fn connect_device( + &self, + address: String, + address_type: AddressType, + timeout_ms: i32, + ) -> Result<(), Box> { + let address_type = match address_type { + AddressType::Public => "public", + AddressType::Random => "random", + }; + + let mut m = vec![MessageItem::DictEntry( + Box::new("Address".into()), + Box::new(MessageItem::Variant(Box::new(address.into()))), + )]; + + m.push(MessageItem::DictEntry( + Box::new("AddressType".into()), + Box::new(MessageItem::Variant(Box::new(address_type.into()))), + )); + + self.call_method( + "ConnectDevice", + Some(&[MessageItem::Array( + MessageItemArray::new(m, Signature::from("a{sv}")).unwrap(), + )]), + timeout_ms, + ) + } +} + +pub enum AddressType { + Public, + Random, } From ea0c29d9d46959083fec689b8cc53966da6e44b1 Mon Sep 17 00:00:00 2001 From: Samer Date: Wed, 24 Jul 2019 19:24:08 +0300 Subject: [PATCH 10/12] set the dbus dependency to a fixed version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a75f028..cf89b0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,5 @@ name = "blurz" path = "src/lib.rs" [dependencies] -dbus = "0.6" +dbus = "= 0.6.4" hex = "0.3" From c6dceb3fb2e70e880be7e1c22615ba50527a0b59 Mon Sep 17 00:00:00 2001 From: Samer Date: Wed, 24 Jul 2019 19:52:37 +0300 Subject: [PATCH 11/12] set dbus and hex to fixed versions --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cf89b0f..98801c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ path = "src/lib.rs" [dependencies] dbus = "= 0.6.4" -hex = "0.3" +hex = "= 0.3.2" From abefbf1497d917a38e66eb9c3762b9432a6bf59a Mon Sep 17 00:00:00 2001 From: Erez Shaul Date: Mon, 25 Nov 2019 09:42:12 +0200 Subject: [PATCH 12/12] add timeout for device.pair function --- src/bluetooth_device.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bluetooth_device.rs b/src/bluetooth_device.rs index 886402f..e0f2991 100644 --- a/src/bluetooth_device.rs +++ b/src/bluetooth_device.rs @@ -295,8 +295,8 @@ impl<'a> BluetoothDevice<'a> { } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n70 - pub fn pair(&self) -> Result<(), Box> { - self.call_method("Pair", None, 60000) + pub fn pair(&self, timeout_ms: i32) -> Result<(), Box> { + self.call_method("Pair", None, timeout_ms) } // http://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/device-api.txt#n97