Buttplug sex toy control library
at dev 4.8 kB view raw
1// Buttplug Rust Source Code File - See https://buttplug.io for more info. 2// 3// Copyright 2016-2024 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, HardwareReadCmd, HardwareWriteCmd}, 10 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer, ProtocolKeepaliveStrategy}, 11}; 12use async_trait::async_trait; 13use buttplug_core::errors::ButtplugDeviceError; 14use buttplug_server_device_config::Endpoint; 15use buttplug_server_device_config::{ 16 ProtocolCommunicationSpecifier, 17 ServerDeviceDefinition, 18 UserDeviceIdentifier, 19}; 20use std::{ 21 sync::{ 22 Arc, 23 atomic::{AtomicU8, Ordering}, 24 }, 25 time::Duration, 26}; 27use uuid::{Uuid, uuid}; 28 29const SATISFYER_PROTOCOL_UUID: Uuid = uuid!("79a0ed0d-f392-4c48-967e-f4467438c344"); 30 31pub mod setup { 32 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; 33 #[derive(Default)] 34 pub struct SatisfyerIdentifierFactory {} 35 36 impl ProtocolIdentifierFactory for SatisfyerIdentifierFactory { 37 fn identifier(&self) -> &str { 38 "satisfyer" 39 } 40 41 fn create(&self) -> Box<dyn ProtocolIdentifier> { 42 Box::new(super::SatisfyerIdentifier::default()) 43 } 44 } 45} 46 47#[derive(Default)] 48pub struct SatisfyerIdentifier {} 49 50#[async_trait] 51impl ProtocolIdentifier for SatisfyerIdentifier { 52 async fn identify( 53 &mut self, 54 hardware: Arc<Hardware>, 55 specifier: ProtocolCommunicationSpecifier, 56 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> { 57 if let ProtocolCommunicationSpecifier::BluetoothLE(s) = specifier { 58 for md in s.manufacturer_data().iter() { 59 if let Some(data) = md.data() { 60 let device_identifier = format!( 61 "{}", 62 u32::from_be_bytes(data.to_vec().try_into().unwrap_or([0; 4])) 63 ); 64 info!( 65 "Satisfyer Device Identifier (from advertisement): {:?} {}", 66 data, device_identifier 67 ); 68 69 return Ok(( 70 UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)), 71 Box::new(SatisfyerInitializer::default()), 72 )); 73 } 74 } 75 } 76 77 let result = hardware 78 .read_value(&HardwareReadCmd::new( 79 SATISFYER_PROTOCOL_UUID, 80 Endpoint::RxBLEModel, 81 128, 82 500, 83 )) 84 .await?; 85 let device_identifier = format!( 86 "{}", 87 u32::from_be_bytes(result.data().to_vec().try_into().unwrap_or([0; 4])) 88 ); 89 info!( 90 "Satisfyer Device Identifier (from RxBLEModel): {:?} {}", 91 result.data(), 92 device_identifier 93 ); 94 return Ok(( 95 UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)), 96 Box::new(SatisfyerInitializer::default()), 97 )); 98 } 99} 100 101#[derive(Default)] 102pub struct SatisfyerInitializer {} 103 104#[async_trait] 105impl ProtocolInitializer for SatisfyerInitializer { 106 async fn initialize( 107 &mut self, 108 hardware: Arc<Hardware>, 109 device_definition: &ServerDeviceDefinition, 110 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 111 let msg = HardwareWriteCmd::new( 112 &[SATISFYER_PROTOCOL_UUID], 113 Endpoint::Command, 114 vec![0x01], 115 true, 116 ); 117 let info_fut = hardware.write_value(&msg); 118 info_fut.await?; 119 120 let feature_count = device_definition 121 .features() 122 .iter() 123 .filter(|x| x.output().is_some()) 124 .count(); 125 126 Ok(Arc::new(Satisfyer::new(feature_count))) 127 } 128} 129 130pub struct Satisfyer { 131 feature_count: usize, 132 last_command: Arc<Vec<AtomicU8>>, 133} 134 135fn form_command(feature_count: usize, data: Arc<Vec<AtomicU8>>) -> Vec<u8> { 136 data[0..feature_count] 137 .iter() 138 .map(|d| vec![d.load(Ordering::Relaxed); 4]) 139 .collect::<Vec<Vec<u8>>>() 140 .concat() 141} 142 143impl Satisfyer { 144 fn new(feature_count: usize) -> Self { 145 let last_command = Arc::new( 146 (0..feature_count) 147 .map(|_| AtomicU8::new(0)) 148 .collect::<Vec<AtomicU8>>(), 149 ); 150 151 Self { 152 feature_count, 153 last_command, 154 } 155 } 156} 157 158impl ProtocolHandler for Satisfyer { 159 fn keepalive_strategy(&self) -> ProtocolKeepaliveStrategy { 160 ProtocolKeepaliveStrategy::RepeatLastPacketStrategyWithTiming(Duration::from_secs(3)) 161 } 162 163 fn handle_output_vibrate_cmd( 164 &self, 165 feature_index: u32, 166 _feature_id: Uuid, 167 speed: u32, 168 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 169 self.last_command[feature_index as usize].store(speed as u8, Ordering::Relaxed); 170 let data = form_command(self.feature_count, self.last_command.clone()); 171 172 Ok(vec![ 173 HardwareWriteCmd::new(&[SATISFYER_PROTOCOL_UUID], Endpoint::Tx, data, false).into(), 174 ]) 175 } 176}