Buttplug sex toy control library
at dev 6.1 kB view raw
1// Buttplug Rust Source Code File - See https://buttplug.io for more info. 2// 3// Copyright 2016-2023 Nonpolynomial Labs LLC. All rights reserved. 4// 5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root 6// for full license information. 7 8use crate::device::{ 9 hardware::{Hardware, HardwareCommand, HardwareEvent, HardwareSubscribeCmd, HardwareWriteCmd}, 10 protocol::{ 11 ProtocolHandler, 12 ProtocolIdentifier, 13 ProtocolInitializer, 14 generic_protocol_initializer_setup, 15 }, 16}; 17use async_trait::async_trait; 18use buttplug_core::{ 19 errors::ButtplugDeviceError, 20 util::{async_manager, sleep}, 21}; 22use buttplug_server_device_config::{ 23 Endpoint, 24 ProtocolCommunicationSpecifier, 25 ServerDeviceDefinition, 26 UserDeviceIdentifier, 27}; 28use futures::FutureExt; 29use std::{ 30 sync::{ 31 Arc, 32 atomic::{AtomicU8, Ordering}, 33 }, 34 time::Duration, 35}; 36use tokio::select; 37use uuid::{Uuid, uuid}; 38 39const FREDORCH_COMMAND_TIMEOUT_MS: u64 = 100; 40const FREDORCH_ROTORY_PROTOCOL_UUID: Uuid = uuid!("0ec6598a-bfd1-4f47-9738-e8cd8ace6473"); 41generic_protocol_initializer_setup!(FredorchRotary, "fredorch-rotary"); 42 43#[derive(Default)] 44pub struct FredorchRotaryInitializer {} 45 46#[async_trait] 47impl ProtocolInitializer for FredorchRotaryInitializer { 48 async fn initialize( 49 &mut self, 50 hardware: Arc<Hardware>, 51 _: &ServerDeviceDefinition, 52 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 53 warn!( 54 "FredorchRotary device doesn't provide state feedback. If the device beeps twice, it is powered off and must be reconnected before it can be controlled!" 55 ); 56 57 let mut event_receiver = hardware.event_stream(); 58 hardware 59 .subscribe(&HardwareSubscribeCmd::new( 60 FREDORCH_ROTORY_PROTOCOL_UUID, 61 Endpoint::Rx, 62 )) 63 .await?; 64 65 let init: Vec<(String, Vec<u8>)> = vec![ 66 ( 67 "Start the handshake".to_owned(), 68 vec![0x55, 0x03, 0x99, 0x9c, 0xaa], 69 ), 70 ( 71 "Send the password".to_owned(), 72 vec![ 73 0x55, 0x09, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xaa, 74 ], 75 ), 76 ( 77 "Power up the device".to_owned(), 78 vec![0x55, 0x03, 0x1f, 0x22, 0xaa], 79 ), 80 ( 81 "Stop the device".to_owned(), 82 vec![0x55, 0x03, 0x24, 0x27, 0xaa], 83 ), 84 ]; 85 86 for data in init { 87 debug!("FredorchRotary: {} - sent {:?}", data.0, data.1); 88 hardware 89 .write_value(&HardwareWriteCmd::new( 90 &[FREDORCH_ROTORY_PROTOCOL_UUID], 91 Endpoint::Tx, 92 data.1.clone(), 93 false, 94 )) 95 .await?; 96 97 select! { 98 event = event_receiver.recv().fuse() => { 99 if let Ok(HardwareEvent::Notification(_, _, n)) = event { 100 debug!("FredorchRotary: {} - received {:?}", data.0, n); 101 } else { 102 return Err( 103 ButtplugDeviceError::ProtocolSpecificError( 104 "FredorchRotary".to_owned(), 105 "FredorchRotary Device disconnected while initialising.".to_owned(), 106 ) 107 ); 108 } 109 } 110 _ = sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { 111 // The after the password check, we won't get anything 112 } 113 } 114 } 115 116 Ok(Arc::new(FredorchRotary::new(hardware))) 117 } 118} 119 120pub struct FredorchRotary { 121 current_speed: Arc<AtomicU8>, 122 target_speed: Arc<AtomicU8>, 123} 124 125async fn speed_update_handler( 126 device: Arc<Hardware>, 127 current_speed: Arc<AtomicU8>, 128 target_speed: Arc<AtomicU8>, 129) { 130 info!("Entering FredorchRotary Control Loop"); 131 132 loop { 133 let ts = target_speed.load(Ordering::Relaxed); 134 let cs = current_speed.load(Ordering::Relaxed); 135 136 trace!("FredorchRotary: {}c vs {}t", cs, ts); 137 138 if ts != cs { 139 let cmd: u8 = if ts == 0 { 140 0x24 141 } else if ts > cs { 142 0x01 143 } else { 144 0x02 145 }; 146 let update = device 147 .write_value(&HardwareWriteCmd::new( 148 &[FREDORCH_ROTORY_PROTOCOL_UUID], 149 Endpoint::Tx, 150 vec![0x55u8, 0x03, cmd, cmd + 3, 0xaa], 151 false, 152 )) 153 .await; 154 if update.is_ok() { 155 debug!( 156 "FredorchRotary: {}c vs {}t - speed {:?}", 157 cs, 158 ts, 159 if ts == 0 { 160 "STOP" 161 } else if ts > cs { 162 "+1" 163 } else { 164 "-1" 165 } 166 ); 167 current_speed.store( 168 u8::max( 169 if ts == 0 { 170 0 171 } else if ts > cs { 172 cs + 1 173 } else { 174 cs - 1 175 }, 176 0, 177 ), 178 Ordering::Relaxed, 179 ); 180 continue; 181 } else { 182 info!("FredorchRotary update error: {:?}", update.err()); 183 break; 184 } 185 } 186 187 sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).await; 188 } 189 info!("FredorchRotary control loop exiting, most likely due to device disconnection."); 190} 191 192impl FredorchRotary { 193 fn new(device: Arc<Hardware>) -> Self { 194 let current_speed = Arc::new(AtomicU8::new(0)); 195 let target_speed = Arc::new(AtomicU8::new(0)); 196 let current_speed_clone = current_speed.clone(); 197 let target_speed_clone = target_speed.clone(); 198 async_manager::spawn(async move { 199 speed_update_handler(device, current_speed_clone, target_speed_clone).await 200 }); 201 Self { 202 current_speed, 203 target_speed, 204 } 205 } 206} 207 208impl ProtocolHandler for FredorchRotary { 209 fn handle_output_oscillate_cmd( 210 &self, 211 _feature_index: u32, 212 _feature_id: Uuid, 213 speed: u32, 214 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 215 let speed: u8 = speed as u8; 216 self.target_speed.store(speed, Ordering::Relaxed); 217 if speed == 0 { 218 self.current_speed.store(speed, Ordering::Relaxed); 219 Ok(vec![ 220 HardwareWriteCmd::new( 221 &[FREDORCH_ROTORY_PROTOCOL_UUID], 222 Endpoint::Tx, 223 vec![0x55, 0x03, 0x24, 0x27, 0xaa], 224 false, 225 ) 226 .into(), 227 ]) 228 } else { 229 Ok(vec![]) 230 } 231 } 232}