hive_btle/platform/linux/
connection.rs1use bluer::Device;
19use std::sync::Arc;
20use std::time::{Duration, Instant};
21use tokio::sync::RwLock;
22
23use crate::config::BlePhy;
24use crate::error::{BleError, Result};
25use crate::transport::BleConnection;
26use crate::NodeId;
27
28struct ConnectionState {
30 alive: bool,
32 mtu: u16,
34 phy: BlePhy,
36 rssi: Option<i8>,
38}
39
40#[derive(Clone)]
44pub struct BluerConnection {
45 peer_id: NodeId,
47 device: Device,
49 state: Arc<RwLock<ConnectionState>>,
51 connected_at: Instant,
53}
54
55impl BluerConnection {
56 pub(crate) async fn new(peer_id: NodeId, device: Device) -> Result<Self> {
58 let mtu = 23; let state = ConnectionState {
63 alive: true,
64 mtu,
65 phy: BlePhy::Le1M, rssi: None,
67 };
68
69 let conn = Self {
70 peer_id,
71 device,
72 state: Arc::new(RwLock::new(state)),
73 connected_at: Instant::now(),
74 };
75
76 conn.update_rssi().await;
78
79 Ok(conn)
80 }
81
82 pub fn device(&self) -> &Device {
84 &self.device
85 }
86
87 pub async fn update_rssi(&self) {
89 if let Ok(Some(rssi)) = self.device.rssi().await {
90 let mut state = self.state.write().await;
91 state.rssi = Some(rssi as i8);
92 }
93 }
94
95 pub async fn set_mtu(&self, mtu: u16) {
97 let mut state = self.state.write().await;
98 state.mtu = mtu;
99 }
100
101 pub async fn set_phy(&self, phy: BlePhy) {
103 let mut state = self.state.write().await;
104 state.phy = phy;
105 }
106
107 pub async fn mark_dead(&self) {
109 let mut state = self.state.write().await;
110 state.alive = false;
111 }
112
113 pub async fn disconnect(&self) -> Result<()> {
115 self.device
116 .disconnect()
117 .await
118 .map_err(|e| BleError::ConnectionFailed(format!("Failed to disconnect: {}", e)))?;
119 self.mark_dead().await;
120 Ok(())
121 }
122
123 pub async fn discover_services(&self) -> Result<()> {
125 let _ = self.device.services().await;
129 Ok(())
130 }
131
132 pub async fn services(&self) -> Result<Vec<bluer::gatt::remote::Service>> {
134 self.device
135 .services()
136 .await
137 .map_err(|e| BleError::GattError(format!("Failed to get services: {}", e)))
138 }
139
140 pub async fn find_service(
142 &self,
143 uuid: uuid::Uuid,
144 ) -> Result<Option<bluer::gatt::remote::Service>> {
145 let services = self.services().await?;
146 for service in services {
147 if service.uuid().await.ok() == Some(uuid) {
148 return Ok(Some(service));
149 }
150 }
151 Ok(None)
152 }
153
154 pub async fn read_characteristic(
156 &self,
157 service_uuid: uuid::Uuid,
158 char_uuid: uuid::Uuid,
159 ) -> Result<Vec<u8>> {
160 let service = self
161 .find_service(service_uuid)
162 .await?
163 .ok_or_else(|| BleError::ServiceNotFound(service_uuid.to_string()))?;
164
165 let characteristics = service
166 .characteristics()
167 .await
168 .map_err(|e| BleError::GattError(format!("Failed to get characteristics: {}", e)))?;
169
170 for char in characteristics {
171 if char.uuid().await.ok() == Some(char_uuid) {
172 return char.read().await.map_err(|e| {
173 BleError::GattError(format!("Failed to read characteristic: {}", e))
174 });
175 }
176 }
177
178 Err(BleError::CharacteristicNotFound(char_uuid.to_string()))
179 }
180
181 pub async fn write_characteristic(
183 &self,
184 service_uuid: uuid::Uuid,
185 char_uuid: uuid::Uuid,
186 value: &[u8],
187 ) -> Result<()> {
188 let service = self
189 .find_service(service_uuid)
190 .await?
191 .ok_or_else(|| BleError::ServiceNotFound(service_uuid.to_string()))?;
192
193 let characteristics = service
194 .characteristics()
195 .await
196 .map_err(|e| BleError::GattError(format!("Failed to get characteristics: {}", e)))?;
197
198 for char in characteristics {
199 if char.uuid().await.ok() == Some(char_uuid) {
200 return char.write(value).await.map_err(|e| {
201 BleError::GattError(format!("Failed to write characteristic: {}", e))
202 });
203 }
204 }
205
206 Err(BleError::CharacteristicNotFound(char_uuid.to_string()))
207 }
208
209 pub async fn subscribe_characteristic(
211 &self,
212 service_uuid: uuid::Uuid,
213 char_uuid: uuid::Uuid,
214 ) -> Result<impl tokio_stream::Stream<Item = Vec<u8>>> {
215 let service = self
216 .find_service(service_uuid)
217 .await?
218 .ok_or_else(|| BleError::ServiceNotFound(service_uuid.to_string()))?;
219
220 let characteristics = service
221 .characteristics()
222 .await
223 .map_err(|e| BleError::GattError(format!("Failed to get characteristics: {}", e)))?;
224
225 for char in characteristics {
226 if char.uuid().await.ok() == Some(char_uuid) {
227 return char.notify().await.map_err(|e| {
228 BleError::GattError(format!("Failed to subscribe to notifications: {}", e))
229 });
230 }
231 }
232
233 Err(BleError::CharacteristicNotFound(char_uuid.to_string()))
234 }
235}
236
237impl BleConnection for BluerConnection {
238 fn peer_id(&self) -> &NodeId {
239 &self.peer_id
240 }
241
242 fn is_alive(&self) -> bool {
243 if let Ok(state) = self.state.try_read() {
245 state.alive
246 } else {
247 true
249 }
250 }
251
252 fn mtu(&self) -> u16 {
253 if let Ok(state) = self.state.try_read() {
254 state.mtu
255 } else {
256 23 }
258 }
259
260 fn phy(&self) -> BlePhy {
261 if let Ok(state) = self.state.try_read() {
262 state.phy
263 } else {
264 BlePhy::Le1M
265 }
266 }
267
268 fn rssi(&self) -> Option<i8> {
269 if let Ok(state) = self.state.try_read() {
270 state.rssi
271 } else {
272 None
273 }
274 }
275
276 fn connected_duration(&self) -> Duration {
277 self.connected_at.elapsed()
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 }