blob: 9425e7627edf3bbd42eecd647a80bc36c968463f [file] [log] [blame] [edit]
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::collections::HashMap;
use std::error::Error;
use std::sync::Arc;
use objc::runtime::Object;
use crate::adapter::BluetoothAdapter;
use crate::delegate::{bm, bmx};
use crate::framework::{cb, nil, ns};
use crate::utils::{NO_PERIPHERAL_FOUND, NOT_SUPPORTED_ERROR, cbx, nsx, wait};
#[derive(Clone, Debug)]
pub struct BluetoothDevice {
pub(crate) adapter: Arc<BluetoothAdapter>,
pub(crate) peripheral: *mut Object,
}
// TODO: implement std::fmt::Debug and/or std::fmt::Display instead of derive?
impl BluetoothDevice {
pub fn new(adapter: Arc<BluetoothAdapter>, uuid: String) -> BluetoothDevice {
trace!("BluetoothDevice::new");
// NOTE: It can happen that there is no peripheral for the given UUID, in that case
// self.peripheral will be nil and all methods that return a Result will return
// Err(Box::from(NO_PERIPHERAL_FOUND)), while others will return some meaningless value.
let peripheral = Self::peripheral_by_uuid(adapter.delegate, &uuid);
if peripheral == nil {
warn!("BluetoothDevice::new found no peripheral for UUID {}", uuid);
}
BluetoothDevice {
adapter: adapter.clone(),
peripheral,
}
}
fn peripheral_by_uuid(delegate: *mut Object, uuid: &String) -> *mut Object {
let peripherals = bm::delegate_peripherals(delegate);
let keys = ns::dictionary_allkeys(peripherals);
for i in 0..ns::array_count(keys) {
let uuid_nsstring = ns::array_objectatindex(keys, i);
if nsx::string_to_string(uuid_nsstring) == *uuid {
let data = ns::dictionary_objectforkey(peripherals, uuid_nsstring);
return ns::dictionary_objectforkey(
data,
nsx::string_from_str(bm::PERIPHERALDATA_PERIPHERALKEY),
);
}
}
nil
}
pub fn get_id(&self) -> String {
trace!("BluetoothDevice::get_id -> get_address");
self.get_address().unwrap_or_default()
}
pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothDevice::get_address");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
// NOTE: There is no better substitute for address than identifier.
let uuid_string =
nsx::string_to_string(ns::uuid_uuidstring(cb::peer_identifier(self.peripheral)));
debug!("BluetoothDevice::get_address -> {}", uuid_string);
Ok(uuid_string)
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothDevice::get_name");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let name_nsstring = cb::peripheral_name(self.peripheral);
let name = if name_nsstring != nil {
nsx::string_to_string(name_nsstring)
} else {
String::from("")
};
debug!("BluetoothDevice::get_name -> {}", name);
Ok(name)
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothDevice::get_uuids");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let data = bmx::peripheraldata(self.adapter.delegate, self.peripheral)?;
let mut v = vec![];
let cbuuids_nsarray =
ns::dictionary_objectforkey(data, nsx::string_from_str(bm::PERIPHERALDATA_UUIDSKEY));
if cbuuids_nsarray != nil {
for i in 0..ns::array_count(cbuuids_nsarray) {
v.push(cbx::uuid_to_canonical_uuid_string(ns::array_objectatindex(
cbuuids_nsarray,
i,
)));
}
}
debug!("BluetoothDevice::get_uuids -> {:?}", v);
Ok(v)
}
pub fn connect(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDevice::connect");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
cb::centralmanager_connectperipheral(self.adapter.manager, self.peripheral);
Ok(())
}
pub fn disconnect(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDevice::disconnect");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
cb::centralmanager_cancelperipheralconnection(self.adapter.manager, self.peripheral);
Ok(())
}
pub fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothDevice::is_connected");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let state = cb::peripheral_state(self.peripheral);
debug!("BluetoothDevice::is_connected -> {}", state);
Ok(state == cb::PERIPHERALSTATE_CONNECTED)
}
pub fn get_gatt_services(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothDevice::get_gatt_services");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let events = bmx::peripheralevents(self.adapter.delegate, self.peripheral)?;
let key = nsx::string_from_str(bm::PERIPHERALEVENT_SERVICESDISCOVEREDKEY);
wait::wait_or_timeout(|| ns::dictionary_objectforkey(events, key) != nil)?;
let mut v = vec![];
let services = cb::peripheral_services(self.peripheral);
for i in 0..ns::array_count(services) {
let uuid_string = cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(
ns::array_objectatindex(services, i),
));
v.push(uuid_string);
}
debug!("BluetoothDevice::get_gatt_services -> {:?}", v);
Ok(v)
}
// Not supported
pub fn get_rssi(&self) -> Result<i16, Box<dyn Error>> {
warn!("BluetoothDevice::get_rssi not supported by BlurMac");
// TODO: Now available from peripheral data in BluetoothAdapter.
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_tx_power(&self) -> Result<i16, Box<dyn Error>> {
warn!("BluetoothDevice::get_tx_power not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
warn!("BluetoothDevice::get_manufacturer_data not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
warn!("BluetoothDevice::get_service_data not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_icon(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_icon not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_class not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_appearance(&self) -> Result<u16, Box<dyn Error>> {
warn!("BluetoothDevice::get_appearance not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_paired(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_paired not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_trusted(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_trusted not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_blocked(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_blocked not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_alias(&self, _value: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::set_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_legacy_pairing(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_legacy_pairing not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_vendor_id_source not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_vendor_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_product_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_device_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
warn!("BluetoothDevice::get_modalias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn connect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::connect_profile not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn disconnect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::disconnect_profile not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn pair(&self) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::pair not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn cancel_pairing(&self) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::cancel_pairing not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}