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 buttplug_core::errors::ButtplugDeviceError;
9use buttplug_core::message::OutputType;
10use buttplug_server_device_config::Endpoint;
11use buttplug_server_device_config::{
12 ProtocolCommunicationSpecifier,
13 ServerDeviceDefinition,
14 UserDeviceIdentifier,
15};
16
17use crate::device::{
18 hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd},
19 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
20};
21use async_trait::async_trait;
22use std::sync::Arc;
23use std::sync::atomic::{AtomicU8, Ordering};
24use uuid::{Uuid, uuid};
25
26const VIBRATISSIMO_PROTOCOL_UUID: Uuid = uuid!("66ef7aa4-1e6a-4067-9066-dcb53c7647f2");
27
28pub mod setup {
29 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
30 #[derive(Default)]
31 pub struct VibratissimoIdentifierFactory {}
32
33 impl ProtocolIdentifierFactory for VibratissimoIdentifierFactory {
34 fn identifier(&self) -> &str {
35 "vibratissimo"
36 }
37
38 fn create(&self) -> Box<dyn ProtocolIdentifier> {
39 Box::new(super::VibratissimoIdentifier::default())
40 }
41 }
42}
43
44#[derive(Default)]
45pub struct VibratissimoIdentifier {}
46
47#[async_trait]
48impl ProtocolIdentifier for VibratissimoIdentifier {
49 async fn identify(
50 &mut self,
51 hardware: Arc<Hardware>,
52 _: ProtocolCommunicationSpecifier,
53 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
54 let result = hardware
55 .read_value(&HardwareReadCmd::new(
56 VIBRATISSIMO_PROTOCOL_UUID,
57 Endpoint::RxBLEModel,
58 128,
59 500,
60 ))
61 .await?;
62 let ident =
63 String::from_utf8(result.data().to_vec()).unwrap_or_else(|_| hardware.name().to_owned());
64 Ok((
65 UserDeviceIdentifier::new(hardware.address(), "vibratissimo", &Some(ident)),
66 Box::new(VibratissimoInitializer::default()),
67 ))
68 }
69}
70
71#[derive(Default)]
72pub struct VibratissimoInitializer {}
73
74#[async_trait]
75impl ProtocolInitializer for VibratissimoInitializer {
76 async fn initialize(
77 &mut self,
78 _: Arc<Hardware>,
79 def: &ServerDeviceDefinition,
80 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
81 let num_vibrators: u8 = def
82 .features()
83 .iter()
84 .filter(|x| {
85 x.output()
86 .as_ref()
87 .is_some_and(|x| x.contains(OutputType::Vibrate))
88 })
89 .count() as u8;
90 Ok(Arc::new(Vibratissimo::new(num_vibrators)))
91 }
92}
93
94pub struct Vibratissimo {
95 speeds: Vec<AtomicU8>,
96}
97
98impl Vibratissimo {
99 fn new(num_vibrators: u8) -> Self {
100 let speeds: Vec<AtomicU8> = std::iter::repeat_with(AtomicU8::default)
101 .take(num_vibrators as usize)
102 .collect();
103 Self { speeds }
104 }
105}
106
107impl ProtocolHandler for Vibratissimo {
108 fn handle_output_vibrate_cmd(
109 &self,
110 feature_index: u32,
111 feature_id: Uuid,
112 speed: u32,
113 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
114 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
115 let mut data = vec![];
116 for cmd in &self.speeds {
117 data.push(cmd.load(std::sync::atomic::Ordering::Relaxed));
118 }
119 if data.len() == 1 {
120 data.push(0x00);
121 }
122
123 // Put the device in write mode
124 Ok(vec![
125 HardwareWriteCmd::new(&[feature_id], Endpoint::TxMode, vec![0x03, 0xff], false).into(),
126 HardwareWriteCmd::new(&[feature_id], Endpoint::TxVibrate, data, false).into(),
127 ])
128 }
129}