rainmaker_components/ble/
ble_linux.rs1#![cfg(target_os = "linux")]
2use std::sync::atomic::{AtomicBool, Ordering};
3use std::sync::LazyLock;
4
5use crate::ble::base::*;
6use crate::error::Error;
7use bluer::adv;
8use bluer::gatt::local;
9
10pub type ApplicationHandle = ApplicationHandleGeneric<local::ApplicationHandle>;
11pub type AdvertisementHandle = AdvertisementHandleGeneric<adv::AdvertisementHandle>;
12
13static RUNTIME: LazyLock<tokio::runtime::Runtime> =
14 LazyLock::new(|| tokio::runtime::Runtime::new().unwrap());
15
16static BLUER_SESSION: LazyLock<bluer::Session> =
17 LazyLock::new(|| RUNTIME.block_on(bluer::Session::new()).unwrap());
18
19static ADAPTER: LazyLock<bluer::Adapter> = LazyLock::new(|| {
20 RUNTIME
21 .block_on(BLUER_SESSION.default_adapter())
22 .expect("Unable to initialize BLE: Is Bluetooth powered on?")
23});
24
25static ADVERTISING: AtomicBool = AtomicBool::new(false);
27static SERVING: AtomicBool = AtomicBool::new(false);
28
29impl From<Advertisement> for adv::Advertisement {
30 fn from(value: Advertisement) -> Self {
31 let service_uuids = value.service_uuids.into_iter().collect();
32 Self {
33 advertisement_type: adv::Type::Peripheral,
34 service_uuids,
35 discoverable: Some(value.discoverable),
36 local_name: value.device_name,
37 ..Default::default()
38 }
39 }
40}
41
42impl From<Descriptor> for local::Descriptor {
43 fn from(value: Descriptor) -> Self {
44 let read = Some(local::DescriptorRead {
45 read: true,
46 fun: Box::new(move |_req| {
47 let val = value.value.clone();
48 Box::pin(async { Ok(val) })
49 }),
50 ..Default::default()
51 });
52
53 Self {
54 uuid: value.uuid,
55 read,
56 ..Default::default()
57 }
58 }
59}
60
61impl From<Characteristic> for local::Characteristic {
62 fn from(value: Characteristic) -> Self {
63 let uuid = value.uuid;
64
65 let read = value.read.map(|read_func| local::CharacteristicRead {
66 read: true,
67 fun: Box::new(move |_| {
68 let val = read_func();
69 Box::pin(async { Ok(val) })
70 }),
71 ..Default::default()
72 });
73
74 let write = value.write.map(|write_func| local::CharacteristicWrite {
75 write: true,
76 method: local::CharacteristicWriteMethod::Fun(Box::new(move |data, _req| {
77 write_func(data);
78 Box::pin(async { Ok(()) })
79 })),
80 ..Default::default()
81 });
82
83 let descriptors = value.descriptors.into_iter().map(|x| x.into()).collect();
84
85 Self {
86 uuid,
87 read,
88 write,
89 descriptors,
90 ..Default::default()
91 }
92 }
93}
94
95impl From<Service> for local::Service {
96 fn from(value: Service) -> Self {
97 let uuid = value.uuid;
98 let primary = value.primary;
99 let characteristics = value
100 .characteristics
101 .into_iter()
102 .map(move |x| x.into())
103 .collect();
104 Self {
105 uuid,
106 primary,
107 characteristics,
108 ..Default::default()
109 }
110 }
111}
112
113impl GattApplication {
114 pub fn serve(self) -> Result<ApplicationHandle, Error> {
115 if SERVING.load(Ordering::SeqCst) {
116 return Err(Error("Already serving an application".to_string()));
117 }
118
119 let handle = RUNTIME.block_on(ADAPTER.serve_gatt_application(self.into()))?;
120 SERVING.store(true, Ordering::SeqCst);
121 Ok(ApplicationHandleGeneric(handle))
122 }
123}
124
125impl From<GattApplication> for local::Application {
126 fn from(value: GattApplication) -> Self {
127 let services = value.services.into_iter().map(|x| x.into()).collect();
128 Self {
129 services,
130 ..Default::default()
131 }
132 }
133}
134
135impl Advertisement {
136 pub fn advertise(self) -> Result<AdvertisementHandle, Error> {
137 if ADVERTISING.load(Ordering::SeqCst) {
138 return Err(Error("Already Advertising".into()));
139 };
140
141 let new_handle = RUNTIME.block_on(ADAPTER.advertise(self.into()))?;
142 ADVERTISING.store(true, Ordering::SeqCst);
143 Ok(AdvertisementHandleGeneric(new_handle))
144 }
145}
146
147impl<T> Drop for ApplicationHandleGeneric<T> {
148 fn drop(&mut self) {
149 SERVING.store(false, Ordering::SeqCst);
150 }
151}
152
153impl<T> Drop for AdvertisementHandleGeneric<T> {
154 fn drop(&mut self) {
155 ADVERTISING.store(false, Ordering::SeqCst);
156 }
157}