Buttplug sex toy control library
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, HardwareReadCmd, HardwareWriteCmd},
10 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
11};
12use async_trait::async_trait;
13use buttplug_core::{errors::ButtplugDeviceError, message::OutputType};
14use buttplug_server_device_config::{
15 Endpoint,
16 ProtocolCommunicationSpecifier,
17 ServerDeviceDefinition,
18 UserDeviceIdentifier,
19};
20use std::sync::Arc;
21use uuid::{Uuid, uuid};
22
23const HISMITH_MINI_PROTOCOL_UUID: Uuid = uuid!("94befc1a-9859-4bf6-99ee-5678c89237a7");
24const HISMITH_MINI_ROTATE_DIRECTIOM_UUID: Uuid = uuid!("94befc1a-9859-4bf6-99ee-5678c89237a7");
25
26pub mod setup {
27 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
28 #[derive(Default)]
29 pub struct HismithMiniIdentifierFactory {}
30
31 impl ProtocolIdentifierFactory for HismithMiniIdentifierFactory {
32 fn identifier(&self) -> &str {
33 "hismith-mini"
34 }
35
36 fn create(&self) -> Box<dyn ProtocolIdentifier> {
37 Box::new(super::HismithMiniIdentifier::default())
38 }
39 }
40}
41
42#[derive(Default)]
43pub struct HismithMiniIdentifier {}
44
45#[async_trait]
46impl ProtocolIdentifier for HismithMiniIdentifier {
47 async fn identify(
48 &mut self,
49 hardware: Arc<Hardware>,
50 _: ProtocolCommunicationSpecifier,
51 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
52 let result = hardware
53 .read_value(&HardwareReadCmd::new(
54 HISMITH_MINI_PROTOCOL_UUID,
55 Endpoint::RxBLEModel,
56 128,
57 500,
58 ))
59 .await?;
60
61 let identifier = result
62 .data()
63 .iter()
64 .map(|b| format!("{b:02x}"))
65 .collect::<String>();
66 info!("Hismith Device Identifier: {}", identifier);
67
68 Ok((
69 UserDeviceIdentifier::new(hardware.address(), "hismith-mini", &Some(identifier)),
70 Box::new(HismithMiniInitializer::default()),
71 ))
72 }
73}
74
75#[derive(Default)]
76pub struct HismithMiniInitializer {}
77
78#[async_trait]
79impl ProtocolInitializer for HismithMiniInitializer {
80 async fn initialize(
81 &mut self,
82 _: Arc<Hardware>,
83 device_definition: &ServerDeviceDefinition,
84 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
85 Ok(Arc::new(HismithMini {
86 dual_vibe: device_definition
87 .features()
88 .iter()
89 .filter(|x| {
90 x.output()
91 .as_ref()
92 .is_some_and(|x| x.contains(OutputType::Vibrate))
93 })
94 .count()
95 >= 2,
96 second_constrict: device_definition
97 .features()
98 .iter()
99 .position(|x| {
100 x.output()
101 .as_ref()
102 .is_some_and(|x| x.contains(OutputType::Constrict))
103 })
104 .unwrap_or(0)
105 == 1,
106 }))
107 }
108}
109
110#[derive(Default)]
111pub struct HismithMini {
112 dual_vibe: bool,
113 second_constrict: bool,
114}
115
116impl ProtocolHandler for HismithMini {
117 fn handle_output_oscillate_cmd(
118 &self,
119 _feature_index: u32,
120 feature_id: Uuid,
121 speed: u32,
122 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
123 let idx: u8 = 0x03;
124 let speed: u8 = speed as u8;
125
126 Ok(vec![
127 HardwareWriteCmd::new(
128 &[feature_id],
129 Endpoint::Tx,
130 vec![0xCC, idx, speed, speed + idx],
131 false,
132 )
133 .into(),
134 ])
135 }
136
137 fn handle_output_vibrate_cmd(
138 &self,
139 feature_index: u32,
140 feature_id: Uuid,
141 speed: u32,
142 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
143 let idx: u8 = if !self.dual_vibe || feature_index == 1 {
144 0x05
145 } else {
146 0x03
147 };
148 let speed: u8 = speed as u8;
149
150 Ok(vec![
151 HardwareWriteCmd::new(
152 &[feature_id],
153 Endpoint::Tx,
154 vec![0xCC, idx, speed, speed + idx],
155 false,
156 )
157 .into(),
158 ])
159 }
160
161 fn handle_output_constrict_cmd(
162 &self,
163 _feature_index: u32,
164 feature_id: Uuid,
165 level: u32,
166 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
167 let idx: u8 = if self.second_constrict { 0x05 } else { 0x03 };
168 let speed: u8 = level as u8;
169
170 Ok(vec![
171 HardwareWriteCmd::new(
172 &[feature_id],
173 Endpoint::Tx,
174 vec![0xCC, idx, speed, speed + idx],
175 false,
176 )
177 .into(),
178 ])
179 }
180
181 fn handle_output_rotate_cmd(
182 &self,
183 _feature_index: u32,
184 feature_id: Uuid,
185 speed: i32,
186 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
187 Ok(vec![
188 HardwareWriteCmd::new(
189 &[feature_id],
190 Endpoint::Tx,
191 vec![
192 0xCC,
193 0x03,
194 speed.unsigned_abs() as u8,
195 speed.unsigned_abs() as u8 + 3,
196 ],
197 false,
198 )
199 .into(),
200 HardwareWriteCmd::new(
201 &[HISMITH_MINI_ROTATE_DIRECTIOM_UUID],
202 Endpoint::Tx,
203 vec![
204 0xCC,
205 0x01,
206 if speed >= 0 { 0xc0 } else { 0xc1 },
207 if speed >= 0 { 0xc1 } else { 0xc2 },
208 ],
209 false,
210 )
211 .into(),
212 ])
213 }
214
215 fn handle_output_spray_cmd(
216 &self,
217 _feature_index: u32,
218 feature_id: Uuid,
219 _level: u32,
220 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
221 Ok(vec![
222 HardwareWriteCmd::new(
223 &[feature_id],
224 Endpoint::Tx,
225 vec![0xcc, 0x0b, 0x01, 0x0c],
226 false,
227 )
228 .into(),
229 ])
230 }
231}