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 crate::message::{
9 ServerDeviceMessageAttributesV3,
10 ServerGenericDeviceMessageAttributesV3,
11 v1::NullDeviceMessageAttributesV1,
12};
13use buttplug_core::message::{InputType, OutputType};
14
15use buttplug_server_device_config::ServerDeviceFeature;
16
17use getset::{CopyGetters, Getters, Setters};
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Debug, PartialEq, Eq, Serialize, Getters, Setters)]
21pub struct ServerDeviceMessageAttributesV2 {
22 // Generic commands
23 #[getset(get = "pub")]
24 #[serde(rename = "VibrateCmd")]
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub(in crate::message) vibrate_cmd: Option<ServerGenericDeviceMessageAttributesV2>,
27 #[getset(get = "pub")]
28 #[serde(rename = "RotateCmd")]
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub(in crate::message) rotate_cmd: Option<ServerGenericDeviceMessageAttributesV2>,
31 #[getset(get = "pub")]
32 #[serde(rename = "LinearCmd")]
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub(in crate::message) linear_cmd: Option<ServerGenericDeviceMessageAttributesV2>,
35 #[getset(get = "pub")]
36 #[serde(rename = "BatteryLevelCmd")]
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub(in crate::message) battery_level_cmd: Option<ServerSensorDeviceMessageAttributesV2>,
39
40 // RSSILevel is added post-serialization (only for bluetooth devices)
41 #[getset(get = "pub")]
42 #[serde(rename = "RSSILevelCmd")]
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub(in crate::message) rssi_level_cmd: Option<ServerSensorDeviceMessageAttributesV2>,
45
46 // StopDeviceCmd always exists
47 #[getset(get = "pub")]
48 #[serde(rename = "StopDeviceCmd")]
49 pub(in crate::message) stop_device_cmd: NullDeviceMessageAttributesV1,
50
51 // Needed to load from config for fallback, but unused here.
52 #[getset(get = "pub")]
53 #[serde(rename = "FleshlightLaunchFW12Cmd")]
54 #[serde(skip)]
55 pub(in crate::message) fleshlight_launch_fw12_cmd: Option<NullDeviceMessageAttributesV1>,
56 #[getset(get = "pub")]
57 #[serde(rename = "VorzeA10CycloneCmd")]
58 #[serde(skip)]
59 pub(in crate::message) vorze_a10_cyclone_cmd: Option<NullDeviceMessageAttributesV1>,
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Getters, CopyGetters, Setters)]
63pub struct ServerGenericDeviceMessageAttributesV2 {
64 #[getset(get_copy = "pub")]
65 #[serde(rename = "FeatureCount")]
66 pub(in crate::message) feature_count: u32,
67 #[getset(get = "pub")]
68 #[serde(rename = "StepCount")]
69 pub(in crate::message) step_count: Vec<u32>,
70 #[getset(get = "pub")]
71 #[serde(skip)]
72 pub(in crate::message) features: Vec<ServerDeviceFeature>,
73}
74
75#[derive(Clone, Debug, PartialEq, Eq, Serialize, Getters, Setters)]
76pub struct ServerSensorDeviceMessageAttributesV2 {
77 #[getset(get = "pub")]
78 #[serde(skip)]
79 feature: ServerDeviceFeature,
80}
81
82impl ServerSensorDeviceMessageAttributesV2 {
83 pub fn new(feature: &ServerDeviceFeature) -> Self {
84 Self {
85 feature: feature.clone(),
86 }
87 }
88}
89
90impl From<Vec<ServerDeviceFeature>> for ServerDeviceMessageAttributesV2 {
91 fn from(value: Vec<ServerDeviceFeature>) -> Self {
92 ServerDeviceMessageAttributesV3::from(value).into()
93 }
94}
95
96pub fn vibrate_cmd_from_scalar_cmd(
97 attributes_vec: &[ServerGenericDeviceMessageAttributesV3],
98) -> ServerGenericDeviceMessageAttributesV2 {
99 let mut feature_count = 0u32;
100 let mut step_count = vec![];
101 let mut features = vec![];
102 for attr in attributes_vec {
103 if *attr.actuator_type() == OutputType::Vibrate {
104 feature_count += 1;
105 step_count.push(*attr.step_count());
106 features.push(attr.feature().clone());
107 }
108 }
109 ServerGenericDeviceMessageAttributesV2 {
110 feature_count,
111 step_count,
112 features,
113 }
114}
115
116impl From<ServerDeviceMessageAttributesV3> for ServerDeviceMessageAttributesV2 {
117 fn from(other: ServerDeviceMessageAttributesV3) -> Self {
118 Self {
119 vibrate_cmd: other
120 .scalar_cmd()
121 .as_ref()
122 .map(|x| vibrate_cmd_from_scalar_cmd(x))
123 .filter(|x| x.feature_count() != 0),
124 rotate_cmd: other
125 .rotate_cmd()
126 .as_ref()
127 .map(|x| ServerGenericDeviceMessageAttributesV2::from(x.clone())),
128 linear_cmd: other
129 .linear_cmd()
130 .as_ref()
131 .map(|x| ServerGenericDeviceMessageAttributesV2::from(x.clone())),
132 battery_level_cmd: {
133 if let Some(sensor_info) = other.sensor_read_cmd() {
134 sensor_info
135 .iter()
136 .find(|x| *x.sensor_type() == InputType::Battery)
137 .map(|attr| ServerSensorDeviceMessageAttributesV2::new(attr.feature()))
138 } else {
139 None
140 }
141 },
142 rssi_level_cmd: {
143 if let Some(sensor_info) = other.sensor_read_cmd() {
144 sensor_info
145 .iter()
146 .find(|x| *x.sensor_type() == InputType::Rssi)
147 .map(|attr| ServerSensorDeviceMessageAttributesV2::new(attr.feature()))
148 } else {
149 None
150 }
151 },
152 stop_device_cmd: other.stop_device_cmd().clone(),
153 fleshlight_launch_fw12_cmd: other.fleshlight_launch_fw12_cmd().clone(),
154 vorze_a10_cyclone_cmd: other.vorze_a10_cyclone_cmd().clone(),
155 }
156 }
157}
158
159impl From<Vec<ServerGenericDeviceMessageAttributesV3>> for ServerGenericDeviceMessageAttributesV2 {
160 fn from(attributes_vec: Vec<ServerGenericDeviceMessageAttributesV3>) -> Self {
161 Self {
162 feature_count: attributes_vec.len() as u32,
163 step_count: attributes_vec.iter().map(|x| *x.step_count()).collect(),
164 features: attributes_vec.iter().map(|x| x.feature().clone()).collect(),
165 }
166 }
167}