rainmaker_components/ble/
ble_linux.rs

1#![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
25/* Advertising and serving can only be done at one place at a time */
26static 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}