Buttplug sex toy control library
at dev 4.1 kB view raw
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::{lelo_harmony::LeloHarmony, lelof1s::LeloF1s}; 9use crate::device::{ 10 hardware::{ 11 Hardware, 12 HardwareEvent, 13 HardwareSubscribeCmd, 14 HardwareUnsubscribeCmd, 15 HardwareWriteCmd, 16 }, 17 protocol::{ 18 ProtocolHandler, 19 ProtocolIdentifier, 20 ProtocolInitializer, 21 generic_protocol_initializer_setup, 22 }, 23}; 24use async_trait::async_trait; 25use buttplug_core::errors::ButtplugDeviceError; 26use buttplug_server_device_config::Endpoint; 27use buttplug_server_device_config::{ 28 ProtocolCommunicationSpecifier, 29 ServerDeviceDefinition, 30 UserDeviceIdentifier, 31}; 32use std::sync::Arc; 33use uuid::{Uuid, uuid}; 34 35const LELO_F1S_V2_PROTOCOL_UUID: Uuid = uuid!("85c59ac5-89ee-4549-8958-ce5449226a5c"); 36generic_protocol_initializer_setup!(LeloF1sV2, "lelo-f1sv2"); 37 38#[derive(Default)] 39pub struct LeloF1sV2Initializer {} 40 41#[async_trait] 42impl ProtocolInitializer for LeloF1sV2Initializer { 43 async fn initialize( 44 &mut self, 45 hardware: Arc<Hardware>, 46 _: &ServerDeviceDefinition, 47 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 48 let use_harmony = !hardware.endpoints().contains(&Endpoint::Whitelist); 49 let sec_endpoint = if use_harmony { 50 Endpoint::Generic0 51 } else { 52 Endpoint::Whitelist 53 }; 54 55 // The Lelo F1s V2 has a very specific pairing flow: 56 // * First the device is turned on in BLE mode (long press) 57 // * Then the security endpoint (Whitelist) needs to be read (which we can do via subscribe) 58 // * If it returns 0x00,00,00,00,00,00,00,00 the connection isn't not authorised 59 // * To authorize, the password must be writen to the characteristic. 60 // * If the password is unknown (buttplug lacks a storage mechanism right now), the power button 61 // must be pressed to send the password 62 // * The password must not be sent whilst subscribed to the endpoint 63 // * Once the password has been sent, the endpoint can be read for status again 64 // * If it returns 0x00,00,00,00,00,00,00,00 the connection is authorised 65 let mut event_receiver = hardware.event_stream(); 66 hardware 67 .subscribe(&HardwareSubscribeCmd::new( 68 LELO_F1S_V2_PROTOCOL_UUID, 69 sec_endpoint, 70 )) 71 .await?; 72 let noauth: Vec<u8> = vec![0; 8]; 73 let authed: Vec<u8> = vec![1, 0, 0, 0, 0, 0, 0, 0]; 74 75 loop { 76 let event = event_receiver.recv().await; 77 if let Ok(HardwareEvent::Notification(_, _, n)) = event { 78 if n.eq(&noauth) { 79 info!( 80 "Lelo F1s V2 isn't authorised: Tap the device's power button to complete connection." 81 ) 82 } else if n.eq(&authed) { 83 debug!("Lelo F1s V2 is authorised!"); 84 if use_harmony { 85 return Ok(Arc::new(LeloHarmony::default())); 86 } else { 87 return Ok(Arc::new(LeloF1s::new(true))); 88 } 89 } else { 90 debug!("Lelo F1s V2 gave us a password: {:?}", n); 91 // Can't send whilst subscribed 92 hardware 93 .unsubscribe(&HardwareUnsubscribeCmd::new( 94 LELO_F1S_V2_PROTOCOL_UUID, 95 sec_endpoint, 96 )) 97 .await?; 98 // Send with response 99 hardware 100 .write_value(&HardwareWriteCmd::new( 101 &[LELO_F1S_V2_PROTOCOL_UUID], 102 sec_endpoint, 103 n, 104 true, 105 )) 106 .await?; 107 // Get back to the loop 108 hardware 109 .subscribe(&HardwareSubscribeCmd::new( 110 LELO_F1S_V2_PROTOCOL_UUID, 111 sec_endpoint, 112 )) 113 .await?; 114 } 115 } else { 116 return Err(ButtplugDeviceError::ProtocolSpecificError( 117 "LeloF1sV2".to_owned(), 118 "Lelo F1s V2 didn't provided valid security handshake".to_owned(), 119 )); 120 } 121 } 122 } 123}