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 super::hismith_mini::HismithMiniInitializer;
9use crate::device::{
10 hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd},
11 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
12};
13use async_trait::async_trait;
14use buttplug_core::errors::ButtplugDeviceError;
15use buttplug_server_device_config::Endpoint;
16use buttplug_server_device_config::{
17 ProtocolCommunicationSpecifier,
18 ServerDeviceDefinition,
19 UserDeviceIdentifier,
20};
21use std::sync::Arc;
22use uuid::{Uuid, uuid};
23
24const HISMITH_PROTOCOL_UUID: Uuid = uuid!("e59f9c5d-bb4a-4a9c-ab57-0ceb43af1da7");
25
26pub mod setup {
27 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
28 #[derive(Default)]
29 pub struct HismithIdentifierFactory {}
30
31 impl ProtocolIdentifierFactory for HismithIdentifierFactory {
32 fn identifier(&self) -> &str {
33 "hismith"
34 }
35
36 fn create(&self) -> Box<dyn ProtocolIdentifier> {
37 Box::new(super::HismithIdentifier::default())
38 }
39 }
40}
41
42#[derive(Default)]
43pub struct HismithIdentifier {}
44
45const LEGACY_HISMITHS: [&str; 6] = ["1001", "1002", "1003", "3001", "2001", "1006"];
46
47#[async_trait]
48impl ProtocolIdentifier for HismithIdentifier {
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 HISMITH_PROTOCOL_UUID,
57 Endpoint::RxBLEModel,
58 128,
59 500,
60 ))
61 .await?;
62
63 let identifier = result
64 .data()
65 .iter()
66 .map(|b| format!("{b:02x}"))
67 .collect::<String>();
68 info!("Hismith Device Identifier: {}", identifier);
69
70 if !LEGACY_HISMITHS.contains(&identifier.as_str()) {
71 info!("Not a legacy Hismith, using hismith-mini protocol");
72 return Ok((
73 UserDeviceIdentifier::new(hardware.address(), "hismith-mini", &Some(identifier)),
74 Box::new(HismithMiniInitializer::default()),
75 ));
76 }
77
78 Ok((
79 UserDeviceIdentifier::new(hardware.address(), "hismith", &Some(identifier)),
80 Box::new(HismithInitializer::default()),
81 ))
82 }
83}
84
85#[derive(Default)]
86pub struct HismithInitializer {}
87
88#[async_trait]
89impl ProtocolInitializer for HismithInitializer {
90 async fn initialize(
91 &mut self,
92 _: Arc<Hardware>,
93 _: &ServerDeviceDefinition,
94 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
95 Ok(Arc::new(Hismith::default()))
96 }
97}
98
99#[derive(Default)]
100pub struct Hismith {}
101
102impl ProtocolHandler for Hismith {
103 fn handle_output_oscillate_cmd(
104 &self,
105 _feature_index: u32,
106 feature_id: Uuid,
107 speed: u32,
108 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
109 let idx: u8 = 0x04;
110 let speed: u8 = speed as u8;
111
112 Ok(vec![
113 HardwareWriteCmd::new(
114 &[feature_id],
115 Endpoint::Tx,
116 vec![0xAA, idx, speed, speed + idx],
117 false,
118 )
119 .into(),
120 ])
121 }
122
123 fn handle_output_vibrate_cmd(
124 &self,
125 feature_index: u32,
126 feature_id: Uuid,
127 speed: u32,
128 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
129 // Wildolo has a vibe at index 0 using id 4
130 // The thrusting stroker has a vibe at index 1 using id 6 (and the weird 0xf0 off)
131 let idx: u8 = if feature_index == 0 { 0x04 } else { 0x06 };
132 let speed: u8 = if feature_index != 0 && speed == 0 {
133 0xf0
134 } else {
135 speed as u8
136 };
137
138 Ok(vec![
139 HardwareWriteCmd::new(
140 &[feature_id],
141 Endpoint::Tx,
142 vec![0xAA, idx, speed, speed + idx],
143 false,
144 )
145 .into(),
146 ])
147 }
148}