Buttplug sex toy control library
1pub mod communication; 2use std::{collections::HashSet, fmt::Debug, sync::Arc, time::Duration}; 3 4use async_trait::async_trait; 5use buttplug_core::errors::ButtplugDeviceError; 6use buttplug_server_device_config::{Endpoint, ProtocolCommunicationSpecifier}; 7use futures::future::BoxFuture; 8use futures_util::FutureExt; 9use getset::{CopyGetters, Getters}; 10use instant::Instant; 11use serde::{Deserialize, Serialize}; 12use tokio::sync::{RwLock, broadcast}; 13use uuid::Uuid; 14 15/// Parameters for reading data from a [Hardware](crate::device::Hardware) endpoint 16/// 17/// Low level read command structure, used by 18/// [ButtplugProtocol](crate::device::protocol::ButtplugProtocol) implementations when working with 19/// [Hardware](crate::device::Hardware) structures. 20#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize, CopyGetters)] 21#[getset(get_copy = "pub")] 22pub struct HardwareReadCmd { 23 /// Feature ID for reading 24 #[serde(default)] 25 command_id: Uuid, 26 /// Endpoint to read from 27 endpoint: Endpoint, 28 /// Amount of data to read from endpoint 29 length: u32, 30 /// Timeout for reading data 31 timeout_ms: u32, 32} 33 34impl HardwareReadCmd { 35 /// Creates a new DeviceReadCmd instance 36 pub fn new(command_id: Uuid, endpoint: Endpoint, length: u32, timeout_ms: u32) -> Self { 37 Self { 38 command_id, 39 endpoint, 40 length, 41 timeout_ms, 42 } 43 } 44} 45 46/// Parameters for writing data to a [Hardware](crate::device::Hardware) endpoint 47/// 48/// Low level write command structure, used by 49/// [ButtplugProtocol](crate::device::protocol::ButtplugProtocol) implementations when working with 50/// [Hardware](crate::device::Hardware) structures. 51#[derive(Eq, Debug, Clone, Serialize, Deserialize, Getters, CopyGetters)] 52pub struct HardwareWriteCmd { 53 /// Feature ID for this command. As a write command can possibly write to multiple features in one 54 /// call, this can have multiple feature IDs. 55 #[getset(get = "pub")] 56 #[serde(default)] 57 command_id: HashSet<Uuid>, 58 /// Endpoint to write to 59 #[getset(get_copy = "pub")] 60 endpoint: Endpoint, 61 /// Data to write to endpoint 62 #[getset(get = "pub")] 63 data: Vec<u8>, 64 /// Only used with Bluetooth LE writing. If true, use WriteWithResponse commands when sending data to device. 65 #[getset(get_copy = "pub")] 66 write_with_response: bool, 67} 68 69impl PartialEq for HardwareWriteCmd { 70 fn eq(&self, other: &Self) -> bool { 71 self.endpoint() == other.endpoint() 72 && self.data() == other.data() 73 && self.write_with_response() == other.write_with_response() 74 } 75} 76 77impl HardwareWriteCmd { 78 /// Create a new DeviceWriteCmd instance. 79 pub fn new( 80 command_id: &[Uuid], 81 endpoint: Endpoint, 82 data: Vec<u8>, 83 write_with_response: bool, 84 ) -> Self { 85 Self { 86 command_id: HashSet::from_iter(command_id.iter().cloned()), 87 endpoint, 88 data, 89 write_with_response, 90 } 91 } 92} 93 94/// Parameters for subscribing to a [Hardware](crate::device::Hardware) endpoint 95/// 96/// Low level subscribe structure, used by 97/// [ButtplugProtocol](crate::device::protocol::ButtplugProtocol) implementations when working with 98/// [Hardware](crate::device::Hardware) structures. 99/// 100/// While usually related to notify/indicate characteristics on Bluetooth LE devices, can be used 101/// with any read endpoint to signal that any information received should be automatically passed to 102/// the protocol implementation. 103#[derive(Eq, Debug, Clone, Copy, Serialize, Deserialize, CopyGetters)] 104#[getset(get_copy = "pub")] 105pub struct HardwareSubscribeCmd { 106 /// Feature ID for this command 107 #[getset(get_copy = "pub")] 108 #[serde(default)] 109 command_id: Uuid, 110 /// Endpoint to subscribe to notifications from. 111 endpoint: Endpoint, 112} 113 114impl PartialEq for HardwareSubscribeCmd { 115 fn eq(&self, other: &Self) -> bool { 116 self.endpoint() == other.endpoint() 117 } 118} 119 120impl HardwareSubscribeCmd { 121 /// Create a new DeviceSubscribeCmd instance 122 pub fn new(command_id: Uuid, endpoint: Endpoint) -> Self { 123 Self { 124 command_id, 125 endpoint, 126 } 127 } 128} 129 130/// Parameters for unsubscribing from a [Hardware](crate::device::Hardware) endpoint that has 131/// previously been subscribed. 132/// 133/// Low level subscribe structure, used by 134/// [ButtplugProtocol](crate::device::protocol::ButtplugProtocol) implementations when working with 135/// [Hardware](crate::device::Hardware) structures. 136#[derive(Eq, Debug, Clone, Copy, Serialize, Deserialize, CopyGetters)] 137#[getset(get_copy = "pub")] 138pub struct HardwareUnsubscribeCmd { 139 #[serde(default)] 140 command_id: Uuid, 141 endpoint: Endpoint, 142} 143 144impl PartialEq for HardwareUnsubscribeCmd { 145 fn eq(&self, other: &Self) -> bool { 146 self.endpoint() == other.endpoint() 147 } 148} 149 150impl HardwareUnsubscribeCmd { 151 /// Create a new DeviceUnsubscribeCmd instance 152 pub fn new(command_id: Uuid, endpoint: Endpoint) -> Self { 153 Self { 154 command_id, 155 endpoint, 156 } 157 } 158} 159 160/// Enumeration of all possible commands that can be sent to a 161/// [Hardware](crate::device::Hardware). 162#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)] 163pub enum HardwareCommand { 164 Write(HardwareWriteCmd), 165 // Read not included here because it needs to be called directly so the response can be handled. 166 Subscribe(HardwareSubscribeCmd), 167 Unsubscribe(HardwareUnsubscribeCmd), 168} 169 170impl HardwareCommand { 171 pub fn overlaps(&self, command: &HardwareCommand) -> bool { 172 // There is probably a cleaner way to write these match branches to drop the if/else and default 173 // out to false, but I can't figure it out right now. 174 match self { 175 HardwareCommand::Write(c) => { 176 if let HardwareCommand::Write(write) = command { 177 c.command_id().intersection(&write.command_id).count() > 0 178 } else { 179 false 180 } 181 } 182 HardwareCommand::Subscribe(c) => { 183 if let HardwareCommand::Subscribe(sub) = command { 184 c.command_id() == sub.command_id 185 } else { 186 false 187 } 188 } 189 HardwareCommand::Unsubscribe(c) => { 190 if let HardwareCommand::Unsubscribe(sub) = command { 191 c.command_id() == sub.command_id 192 } else { 193 false 194 } 195 } 196 } 197 } 198} 199 200impl From<HardwareWriteCmd> for HardwareCommand { 201 fn from(msg: HardwareWriteCmd) -> Self { 202 HardwareCommand::Write(msg) 203 } 204} 205 206impl From<HardwareSubscribeCmd> for HardwareCommand { 207 fn from(msg: HardwareSubscribeCmd) -> Self { 208 HardwareCommand::Subscribe(msg) 209 } 210} 211 212impl From<HardwareUnsubscribeCmd> for HardwareCommand { 213 fn from(msg: HardwareUnsubscribeCmd) -> Self { 214 HardwareCommand::Unsubscribe(msg) 215 } 216} 217 218#[derive(Debug, Clone, Getters)] 219#[getset(get = "pub")] 220pub struct HardwareReading { 221 endpoint: Endpoint, 222 data: Vec<u8>, 223} 224 225impl HardwareReading { 226 pub fn new(endpoint: Endpoint, data: &[u8]) -> Self { 227 Self { 228 endpoint, 229 data: data.to_vec(), 230 } 231 } 232} 233 234/// Events that can be emitted from a [Hardware](crate::device::Hardware). 235#[derive(Debug, Clone)] 236pub enum HardwareEvent { 237 /// Device received data 238 Notification(String, Endpoint, Vec<u8>), 239 /// Device disconnected 240 Disconnected(String), 241} 242 243/// Hardware implementation and communication portion of a 244/// [ButtplugDevice](crate::device::ButtplugDevice) instance. The Hardware contains a 245/// HardwareInternal, which handles all of the actual hardware communication. However, the struct 246/// also needs to carry around identifying information, so we wrap it in this type instead of 247/// requiring that all implementors of deal with name/address/endpoint accessors. 248#[derive(CopyGetters, Getters)] 249pub struct Hardware { 250 /// Device name 251 #[getset(get = "pub")] 252 name: String, 253 /// Device address 254 #[getset(get = "pub")] 255 address: String, 256 /// Communication endpoints 257 #[getset(get = "pub")] 258 endpoints: Vec<Endpoint>, 259 /// Minimum time between two packets being sent to the device. Used to deal with congestion across 260 /// protocols like Bluetooth LE, which have guaranteed delivery but can be overloaded due to 261 /// connection intervals. 262 #[getset(get_copy = "pub")] 263 message_gap: Option<Duration>, 264 /// Internal implementation details 265 internal_impl: Box<dyn HardwareInternal>, 266 /// Requires a keepalive signal to be sent by the Server Device class 267 #[getset(get_copy = "pub")] 268 requires_keepalive: bool, 269 last_write_time: Arc<RwLock<Instant>>, 270} 271 272impl Hardware { 273 pub fn new( 274 name: &str, 275 address: &str, 276 endpoints: &[Endpoint], 277 message_gap: &Option<Duration>, 278 requires_keepalive: bool, 279 internal_impl: Box<dyn HardwareInternal>, 280 ) -> Self { 281 Self { 282 name: name.to_owned(), 283 address: address.to_owned(), 284 endpoints: endpoints.into(), 285 message_gap: *message_gap, 286 internal_impl, 287 requires_keepalive, 288 last_write_time: Arc::new(RwLock::new(Instant::now())), 289 } 290 } 291 292 pub async fn time_since_last_write(&self) -> Duration { 293 Instant::now().duration_since(*self.last_write_time.read().await) 294 } 295 296 /// Returns a receiver for any events the device may emit. 297 /// 298 /// This uses a broadcast channel and can be called multiple times to create multiple streams if 299 /// needed. 300 pub fn event_stream(&self) -> broadcast::Receiver<HardwareEvent> { 301 self.internal_impl.event_stream() 302 } 303 304 /// Disconnect from the device (if it is connected) 305 pub fn disconnect(&self) -> BoxFuture<'static, Result<(), ButtplugDeviceError>> { 306 self.internal_impl.disconnect() 307 } 308 309 pub fn parse_message( 310 &self, 311 command: &HardwareCommand, 312 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>> { 313 match command { 314 HardwareCommand::Write(cmd) => self.write_value(cmd), 315 HardwareCommand::Subscribe(cmd) => self.subscribe(cmd), 316 HardwareCommand::Unsubscribe(cmd) => self.unsubscribe(cmd), 317 } 318 } 319 320 /// Read a value from the device 321 pub fn read_value( 322 &self, 323 msg: &HardwareReadCmd, 324 ) -> BoxFuture<'static, Result<HardwareReading, ButtplugDeviceError>> { 325 self.internal_impl.read_value(msg) 326 } 327 328 /// Write a value to the device 329 pub fn write_value( 330 &self, 331 msg: &HardwareWriteCmd, 332 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>> { 333 let write_fut = self.internal_impl.write_value(msg); 334 if self.requires_keepalive { 335 let last_write_time = self.last_write_time.clone(); 336 async move { 337 *last_write_time.write().await = Instant::now(); 338 write_fut.await 339 } 340 .boxed() 341 } else { 342 write_fut 343 } 344 } 345 346 /// Subscribe to a device endpoint, if it exists 347 pub fn subscribe( 348 &self, 349 msg: &HardwareSubscribeCmd, 350 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>> { 351 self.internal_impl.subscribe(msg) 352 } 353 354 /// Unsubscribe from a device endpoint, if it exists 355 pub fn unsubscribe( 356 &self, 357 msg: &HardwareUnsubscribeCmd, 358 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>> { 359 self.internal_impl.unsubscribe(msg) 360 } 361} 362 363/// Internal representation of device implementations 364/// 365/// This trait is implemented by 366/// [DeviceCommunicationManager](crate::server::device::communication_manager::DeviceCommunicationManager) modules 367/// to represent and communicate with devices. It provides an abstract way to represent devices 368/// without having to consider what type of communication bus they may be using. 369pub trait HardwareInternal: Sync + Send { 370 /// Disconnect from the device (if it is connected) 371 fn disconnect(&self) -> BoxFuture<'static, Result<(), ButtplugDeviceError>>; 372 /// Returns a receiver for any events the device may emit. 373 fn event_stream(&self) -> broadcast::Receiver<HardwareEvent>; 374 /// Read a value from the device 375 fn read_value( 376 &self, 377 msg: &HardwareReadCmd, 378 ) -> BoxFuture<'static, Result<HardwareReading, ButtplugDeviceError>>; 379 /// Write a value to the device 380 fn write_value( 381 &self, 382 msg: &HardwareWriteCmd, 383 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>>; 384 /// Subscribe to a device endpoint, if it exists 385 fn subscribe( 386 &self, 387 msg: &HardwareSubscribeCmd, 388 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>>; 389 /// Unsubscribe from a device endpoint, if it exists 390 fn unsubscribe( 391 &self, 392 msg: &HardwareUnsubscribeCmd, 393 ) -> BoxFuture<'static, Result<(), ButtplugDeviceError>>; 394} 395 396#[async_trait] 397pub trait HardwareConnector: Sync + Send + Debug { 398 /// Return the hardware identifier for the device. Depends on the communication bus type, so may 399 /// be a bluetooth name, serial port name, etc... 400 fn specifier(&self) -> ProtocolCommunicationSpecifier; 401 async fn connect(&mut self) -> Result<Box<dyn HardwareSpecializer>, ButtplugDeviceError>; 402} 403 404#[async_trait] 405pub trait HardwareSpecializer: Sync + Send { 406 /// Try to initialize a device. 407 /// 408 /// Given a 409 /// [ProtocolDeviceConfiguration](crate::server::device::configuration::ProtocolDeviceConfiguration) 410 /// which will contain information about what a protocol needs to communicate with a device, try 411 /// to identify all required endpoints on the hardware. 412 async fn specialize( 413 &mut self, 414 protocol: &[ProtocolCommunicationSpecifier], 415 ) -> Result<Hardware, ButtplugDeviceError>; 416} 417 418/// Used in cases where there's nothing to specialize for the protocol. 419pub struct GenericHardwareSpecializer { 420 hardware: Option<Hardware>, 421} 422 423impl GenericHardwareSpecializer { 424 pub fn new(hardware: Hardware) -> Self { 425 Self { 426 hardware: Some(hardware), 427 } 428 } 429} 430 431#[async_trait] 432impl HardwareSpecializer for GenericHardwareSpecializer { 433 async fn specialize( 434 &mut self, 435 _: &[ProtocolCommunicationSpecifier], 436 ) -> Result<Hardware, ButtplugDeviceError> { 437 Ok(self.hardware.take().expect("This should only be run once")) 438 } 439}