Buttplug sex toy control library
1use buttplug_client::{
2 ButtplugClient,
3 ButtplugClientError,
4 connector::ButtplugRemoteClientConnector,
5 serializer::ButtplugClientJSONSerializer,
6};
7
8use buttplug_core::message::OutputType;
9use buttplug_transport_websocket_tungstenite::ButtplugWebsocketClientTransport;
10use strum::IntoEnumIterator;
11use tokio::io::{self, AsyncBufReadExt, BufReader};
12
13async fn wait_for_input() {
14 BufReader::new(io::stdin())
15 .lines()
16 .next_line()
17 .await
18 .unwrap();
19}
20
21#[tokio::main]
22async fn main() -> anyhow::Result<()> {
23 let connector = ButtplugRemoteClientConnector::<
24 ButtplugWebsocketClientTransport,
25 ButtplugClientJSONSerializer,
26 >::new(ButtplugWebsocketClientTransport::new_insecure_connector(
27 "ws://127.0.0.1:12345",
28 ));
29
30 let client = ButtplugClient::new("Example Client");
31 client.connect(connector).await?;
32
33 println!("Connected!");
34
35 // You usually shouldn't run Start/Stop scanning back-to-back like
36 // this, but with TestDevice we know our device will be found when we
37 // call StartScanning, so we can get away with it.
38 client.start_scanning().await?;
39 client.stop_scanning().await?;
40 println!("Client currently knows about these devices:");
41 let mut device_index: i32 = -1;
42 for (i, device) in client.devices() {
43 device_index = i as i32;
44 println!("- {}", device.name());
45 }
46 wait_for_input().await;
47
48 for (_, device) in client.devices() {
49 println!("{} supports these outputs:", device.name());
50 for output_type in OutputType::iter() {
51 for feature in device.device_features().values() {
52 if let Some(output) = feature.feature().output()
53 && output.contains(output_type)
54 {
55 println!("- {}", output_type);
56 }
57 }
58 }
59 }
60
61 println!("Sending commands");
62
63 // Now that we know the message types for our connected device, we
64 // can send a message over! Seeing as we want to stick with the
65 // modern generic messages, we'll go with VibrateCmd.
66 //
67 // There's a couple of ways to send this message.
68 let devices = client.devices();
69 let test_client_device = devices.get(&(device_index as u32)).unwrap();
70
71 // We can use the convenience functions on ButtplugClientDevice to
72 // send the message. This version sets all of the motors on a
73 // vibrating device to the same speed.
74 test_client_device.vibrate(0.5f64).await?;
75
76 // If we wanted to just set one motor on and the other off, we could
77 // try this version that uses an array. It'll throw an exception if
78 // the array isn't the same size as the number of motors available as
79 // denoted by FeatureCount, though.
80 //
81 // You can get the vibrator count using the following code, though we
82 // know it's 2 so we don't really have to use it.
83 let vibrator_count = test_client_device.vibrate_features().len();
84
85 println!(
86 "{} has {} vibrators.",
87 test_client_device.name(),
88 vibrator_count,
89 );
90
91 // Just set all of the vibrators to full speed.
92 if vibrator_count > 0 {
93 test_client_device.vibrate(10).await?;
94 } else {
95 println!("Device does not have > 1 vibrators, not running multiple vibrator test.");
96 }
97
98 wait_for_input().await;
99 println!("Disconnecting");
100 // And now we disconnect as usual.
101 client.disconnect().await?;
102 println!("Trying error");
103 // If we try to send a command to a device after the client has
104 // disconnected, we'll get an exception thrown.
105 let vibrate_result = test_client_device.vibrate(30).await;
106 if let Err(ButtplugClientError::ButtplugConnectorError(error)) = vibrate_result {
107 println!("Tried to send after disconnection! Error: ");
108 println!("{}", error);
109 }
110 wait_for_input().await;
111
112 Ok(())
113}