Buttplug sex toy control library
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}