buttplug/server/device/protocol/
satisfyer.rs1use crate::{
9 core::{
10 errors::ButtplugDeviceError,
11 message::{self, Endpoint},
12 },
13 server::device::{
14 configuration::{ProtocolCommunicationSpecifier, UserDeviceDefinition, UserDeviceIdentifier},
15 hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd},
16 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
17 },
18 util::{async_manager, sleep},
19};
20use async_trait::async_trait;
21use std::{
22 sync::{
23 atomic::{AtomicU8, Ordering},
24 Arc,
25 },
26 time::Duration,
27};
28
29pub mod setup {
30 use crate::server::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
31 #[derive(Default)]
32 pub struct SatisfyerIdentifierFactory {}
33
34 impl ProtocolIdentifierFactory for SatisfyerIdentifierFactory {
35 fn identifier(&self) -> &str {
36 "satisfyer"
37 }
38
39 fn create(&self) -> Box<dyn ProtocolIdentifier> {
40 Box::new(super::SatisfyerIdentifier::default())
41 }
42 }
43}
44
45#[derive(Default)]
46pub struct SatisfyerIdentifier {}
47
48#[async_trait]
49impl ProtocolIdentifier for SatisfyerIdentifier {
50 async fn identify(
51 &mut self,
52 hardware: Arc<Hardware>,
53 specifier: ProtocolCommunicationSpecifier,
54 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
55 if let ProtocolCommunicationSpecifier::BluetoothLE(s) = specifier {
56 for md in s.manufacturer_data().iter() {
57 if let Some(data) = md.data() {
58 let device_identifier = format!(
59 "{}",
60 u32::from_be_bytes(data.to_vec().try_into().unwrap_or([0; 4]))
61 );
62 info!(
63 "Satisfyer Device Identifier (from advertisement): {:?} {}",
64 data, device_identifier
65 );
66
67 return Ok((
68 UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)),
69 Box::new(SatisfyerInitializer::default()),
70 ));
71 }
72 }
73 }
74
75 let result = hardware
76 .read_value(&HardwareReadCmd::new(Endpoint::RxBLEModel, 128, 500))
77 .await?;
78 let device_identifier = format!(
79 "{}",
80 u32::from_be_bytes(result.data().to_vec().try_into().unwrap_or([0; 4]))
81 );
82 info!(
83 "Satisfyer Device Identifier (from RxBLEModel): {:?} {}",
84 result.data(),
85 device_identifier
86 );
87 return Ok((
88 UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)),
89 Box::new(SatisfyerInitializer::default()),
90 ));
91 }
92}
93
94#[derive(Default)]
95pub struct SatisfyerInitializer {}
96
97#[async_trait]
98impl ProtocolInitializer for SatisfyerInitializer {
99 async fn initialize(
100 &mut self,
101 hardware: Arc<Hardware>,
102 device_definition: &UserDeviceDefinition,
103 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
104 let msg = HardwareWriteCmd::new(Endpoint::Command, vec![0x01], true);
105 let info_fut = hardware.write_value(&msg);
106 info_fut.await?;
107
108 let feature_count = device_definition
109 .features()
110 .iter()
111 .filter(|x| x.actuator().is_some())
112 .count();
113 Ok(Arc::new(Satisfyer::new(hardware, feature_count)))
114 }
115}
116
117pub struct Satisfyer {
118 feature_count: usize,
119 last_command: Arc<Vec<AtomicU8>>,
120}
121
122fn form_command(feature_count: usize, data: Arc<Vec<AtomicU8>>) -> Vec<u8> {
123 data[0..feature_count]
124 .iter()
125 .map(|d| vec![d.load(Ordering::SeqCst); 4])
126 .collect::<Vec<Vec<u8>>>()
127 .concat()
128}
129
130async fn send_satisfyer_updates(
133 device: Arc<Hardware>,
134 feature_count: usize,
135 data: Arc<Vec<AtomicU8>>,
136) {
137 loop {
138 let command = form_command(feature_count, data.clone());
139 if let Err(e) = device
140 .write_value(&HardwareWriteCmd::new(Endpoint::Tx, command, false))
141 .await
142 {
143 error!(
144 "Got an error from a satisfyer device, exiting control loop: {:?}",
145 e
146 );
147 break;
148 }
149 sleep(Duration::from_secs(1)).await;
150 }
151}
152
153impl Satisfyer {
154 fn new(hardware: Arc<Hardware>, feature_count: usize) -> Self {
155 let last_command = Arc::new(
156 (0..feature_count)
157 .map(|_| AtomicU8::new(0))
158 .collect::<Vec<AtomicU8>>(),
159 );
160 let last_command_clone = last_command.clone();
161 async_manager::spawn(async move {
162 send_satisfyer_updates(hardware, feature_count, last_command_clone).await;
163 });
164
165 Self {
166 feature_count,
167 last_command,
168 }
169 }
170}
171
172impl ProtocolHandler for Satisfyer {
173 fn needs_full_command_set(&self) -> bool {
174 true
175 }
176
177 fn handle_scalar_cmd(
178 &self,
179 commands: &[Option<(message::ActuatorType, u32)>],
180 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
181 if self.feature_count != commands.len() {
182 return Err(ButtplugDeviceError::DeviceFeatureCountMismatch(
183 self.feature_count as u32,
184 commands.len() as u32,
185 ));
186 }
187 for (i, item) in commands.iter().enumerate() {
188 let command_val = item.as_ref().unwrap().1 as u8;
189 self.last_command[i].store(command_val, Ordering::SeqCst);
190 }
191 let data = form_command(self.feature_count, self.last_command.clone());
192
193 Ok(vec![HardwareWriteCmd::new(Endpoint::Tx, data, false).into()])
194 }
195}