Buttplug sex toy control library
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add support for MystryVibe Tenuto Mini

authored by

blackspherefollower and committed by
qDot
c48696ec 706e6e64

+194
+49
buttplug/buttplug-device-config/buttplug-device-config.json
··· 1706 1706 } 1707 1707 ] 1708 1708 }, 1709 + "mysteryvibe-v2": { 1710 + "btle": { 1711 + "names": [ 1712 + "6907 MV1" 1713 + ], 1714 + "services": { 1715 + "f0006900-110c-478b-b74b-6f403b364a9c": { 1716 + "txmode": "f0006901-110c-478b-b74b-6f403b364a9c", 1717 + "txvibrate": "f0006903-110c-478b-b74b-6f403b364a9c" 1718 + } 1719 + } 1720 + }, 1721 + "defaults": { 1722 + "name": "Mysteryvibe V2 Device", 1723 + "messages": { 1724 + "ScalarCmd": [ 1725 + { 1726 + "StepRange": [ 1727 + 0, 1728 + 56 1729 + ], 1730 + "ActuatorType": "Vibrate" 1731 + }, 1732 + { 1733 + "StepRange": [ 1734 + 0, 1735 + 56 1736 + ], 1737 + "ActuatorType": "Vibrate" 1738 + }, 1739 + { 1740 + "StepRange": [ 1741 + 0, 1742 + 56 1743 + ], 1744 + "ActuatorType": "Vibrate" 1745 + } 1746 + ] 1747 + } 1748 + }, 1749 + "configurations": [ 1750 + { 1751 + "identifier": [ 1752 + "6907 MV1" 1753 + ], 1754 + "name": "MysteryVibe Tenuto Mini" 1755 + } 1756 + ] 1757 + }, 1709 1758 "picobong": { 1710 1759 "btle": { 1711 1760 "names": [
+22
buttplug/buttplug-device-config/buttplug-device-config.yml
··· 933 933 ActuatorType: Vibrate 934 934 - StepRange: [0, 56] 935 935 ActuatorType: Vibrate 936 + mysteryvibe-v2: 937 + btle: 938 + names: 939 + - "6907 MV1" 940 + services: 941 + f0006900-110c-478b-b74b-6f403b364a9c: 942 + txmode: f0006901-110c-478b-b74b-6f403b364a9c 943 + txvibrate: f0006903-110c-478b-b74b-6f403b364a9c 944 + defaults: 945 + name: Mysteryvibe V2 Device 946 + messages: 947 + ScalarCmd: 948 + - StepRange: [0, 56] 949 + ActuatorType: Vibrate 950 + - StepRange: [0, 56] 951 + ActuatorType: Vibrate 952 + - StepRange: [0, 56] 953 + ActuatorType: Vibrate 954 + configurations: 955 + - identifier: 956 + - "6907 MV1" 957 + name: MysteryVibe Tenuto Mini 936 958 picobong: 937 959 btle: 938 960 names:
+6
buttplug/src/server/device/protocol/mod.rs
··· 57 57 pub mod mizzzee_v2; 58 58 pub mod motorbunny; 59 59 pub mod mysteryvibe; 60 + pub mod mysteryvibe_v2; 61 + 60 62 pub mod nobra; 61 63 pub mod patoo; 62 64 pub mod picobong; ··· 288 290 add_to_protocol_map( 289 291 &mut map, 290 292 mysteryvibe::setup::MysteryVibeIdentifierFactory::default(), 293 + ); 294 + add_to_protocol_map( 295 + &mut map, 296 + mysteryvibe_v2::setup::MysteryVibeV2IdentifierFactory::default(), 291 297 ); 292 298 add_to_protocol_map(&mut map, nobra::setup::NobraIdentifierFactory::default()); 293 299 add_to_protocol_map(&mut map, patoo::setup::PatooIdentifierFactory::default());
+117
buttplug/src/server/device/protocol/mysteryvibe_v2.rs
··· 1 + // Buttplug Rust Source Code File - See https://buttplug.io for more info. 2 + // 3 + // Copyright 2016-2022 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 + 8 + use crate::server::device::configuration::ProtocolDeviceAttributes; 9 + use crate::{ 10 + core::{ 11 + errors::ButtplugDeviceError, 12 + message::{ActuatorType, Endpoint}, 13 + }, 14 + server::device::{ 15 + configuration::ProtocolAttributesType, 16 + hardware::{Hardware, HardwareCommand, HardwareWriteCmd}, 17 + protocol::{ 18 + generic_protocol_initializer_setup, 19 + ProtocolHandler, 20 + ProtocolIdentifier, 21 + ProtocolInitializer, 22 + }, 23 + ServerDeviceIdentifier, 24 + }, 25 + util::{async_manager, sleep} 26 + }; 27 + use async_trait::async_trait; 28 + use std::{sync::Arc, time::Duration}; 29 + use tokio::sync::RwLock; 30 + 31 + generic_protocol_initializer_setup!(MysteryVibeV2, "mysteryvibe-v2"); 32 + 33 + #[derive(Default)] 34 + pub struct MysteryVibeV2Initializer {} 35 + 36 + #[async_trait] 37 + impl ProtocolInitializer for MysteryVibeV2Initializer { 38 + async fn initialize( 39 + &mut self, 40 + hardware: Arc<Hardware>, 41 + _: &ProtocolDeviceAttributes, 42 + ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 43 + let msg = HardwareWriteCmd::new(Endpoint::TxMode, vec![0x03u8, 0x02u8, 0x40u8], true); 44 + hardware.write_value(&msg).await?; 45 + Ok(Arc::new(MysteryVibe::new(hardware))) 46 + } 47 + } 48 + 49 + // Time between Mysteryvibe update commands, in milliseconds. This is basically 50 + // a best guess derived from watching packet timing a few years ago. 51 + // 52 + // Thelemic vibrator. Neat. 53 + // 54 + const MYSTERYVIBE_COMMAND_DELAY_MS: u64 = 93; 55 + 56 + async fn vibration_update_handler(device: Arc<Hardware>, command_holder: Arc<RwLock<Vec<u8>>>) { 57 + info!("Entering Mysteryvibe Control Loop"); 58 + let mut current_command = command_holder.read().await.clone(); 59 + while device 60 + .write_value(&HardwareWriteCmd::new( 61 + Endpoint::TxVibrate, 62 + current_command, 63 + false, 64 + )) 65 + .await 66 + .is_ok() 67 + { 68 + sleep(Duration::from_millis(MYSTERYVIBE_COMMAND_DELAY_MS)).await; 69 + current_command = command_holder.read().await.clone(); 70 + info!("MV Command: {:?}", current_command); 71 + } 72 + info!("Mysteryvibe control loop exiting, most likely due to device disconnection."); 73 + } 74 + 75 + pub struct MysteryVibe { 76 + current_command: Arc<RwLock<Vec<u8>>>, 77 + } 78 + 79 + impl MysteryVibe { 80 + fn new(device: Arc<Hardware>) -> Self { 81 + let current_command = Arc::new(RwLock::new(vec![0u8, 0, 0, 0, 0, 0])); 82 + let current_command_clone = current_command.clone(); 83 + async_manager::spawn( 84 + async move { vibration_update_handler(device, current_command_clone).await }, 85 + ); 86 + Self { current_command } 87 + } 88 + } 89 + 90 + impl ProtocolHandler for MysteryVibe { 91 + fn needs_full_command_set(&self) -> bool { 92 + true 93 + } 94 + 95 + fn handle_scalar_cmd( 96 + &self, 97 + cmds: &[Option<(ActuatorType, u32)>], 98 + ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 99 + let current_command = self.current_command.clone(); 100 + let cmds = cmds.to_vec(); 101 + async_manager::spawn(async move { 102 + let write_mutex = current_command.clone(); 103 + let mut command_writer = write_mutex.write().await; 104 + let command: Vec<u8> = cmds 105 + .into_iter() 106 + .map(|x| x.expect("Validity ensured via GCM match_all").1 as u8) 107 + .collect(); 108 + *command_writer = command; 109 + }); 110 + Ok(vec![]) 111 + } 112 + } 113 + 114 + // TODO Write some tests! 115 + // 116 + // At least, once I figure out how to do that with the weird timing on this 117 + // thing.