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
8//! Management of protocol and device hardware configurations
9//!
10//! Buttplug can handle device communication over several different mediums, including bluetooth,
11//! usb, serial, various network protocols, and others. The library also provides multiple protocols
12//! to communicate with this hardware. All of this information is stored in the
13//! [DeviceConfigurationManager] (aka the DCM), a structure that is built whenever a [buttplug
14//! server](crate::server::ButtplugServer) instance is created, and which is immutable for the life
15//! of the server instance.
16//!
17//! The [DeviceConfigurationManager]'s main job is to take a newly discovered piece of hardware and
18//! figure out if the library supports that hardware. To that end, the [DeviceConfigurationManager]
19//! contains all of the APIs needed to load protocol configurations into the system, as well as
20//! match newly discovered devices to protocols.
21//!
22//! ## Device Identification
23//!
24//! Once devices are connected, they are identified via the following properties:
25//!
26//! - Their communication bus address (BLE address, serial port name, etc... For devices that
27//! connect via network protocols, this may be a generated value, but should be unique.)
28//! - Their protocol name
29//! - Their protocol identifier
30//!
31//! These values are held in [ProtocolDeviceIdentifier] instances, and used around the codebase to
32//! identify a device. This identifier is used so that if a device somehow shares addresses with
33//! another device but identifies under a different protocol, they will still be seen as separate
34//! devices.
35//!
36//! As an example, let's say we have a Lovense Hush. The protocol will be "lovense" (which is
37//! configuration string version of the [Lovense Protocol](crate::device::protocol::lovense) name),
38//! its identifier will be "Z" (the identification letter for Hush in Lovense's proprietary
39//! protocol), and the address will be something like "AA:BB:CC:DD:EE:FF", which is the BLE address
40//! of the device on platforms that provide BLE addresses. Using these 3 values means that, even if
41//! for some reason the BLE address stays the same, if a device identifies differently (say, as a
42//! Domi instead of a Hush), we won't try to reuse the same configuration.
43//!
44//! **NOTE THAT DEVICE IDENTIFIERS MAY NOT BE PORTABLE ACROSS PLATFORMS.** While these are used as
45//! internal identifers as well as keys for user configurations, they may not work if used between,
46//! say, Windows BLE and WebBluetooth, which provide different addressing schemes for devices.
47//!
48//! ## Device Configurations versus User Configurations
49//!
50//! Device Configurations are provided by the core Buttplug Team, and the configuration of all
51//! currently supported devices is both compiled into the library as well as distributed as external
52//! files (see the Device Configuration Files section below). However, users may want to set certain
53//! per-device configurations, in which case, User Configurations can be used.
54//!
55//! User configurations include:
56//!
57//! - Device Allow/Deny Lists: library will either only connect to certain devices, or never connect
58//! to them, respectively.
59//! - Reserved indexes: allows the same device to show up to clients on the same device index every
60//! time it connects
61//! - Device configuration extensions: If a new device from a brand comes out and has not been added
62//! to the main Device Configuration file, or else a user creates their own DIY device that uses
63//! another protocol (hence it will never be in the main Device Configuration file as there may
64//! only be one of the device, period), a user can add an extension to an established protocol to
65//! provide new identifier information.
66//! - User configured message attributes: limits that can be set for certain messages a device
67//! takes. For instance, setting an upper limit on the vibration speed of a vibrator so it will
68//! only go to 80% instead of 100%.
69//!
70//! User configurations can be added to the [DeviceConfigurationManager].
71//!
72//! ## Device Configuration Files
73//!
74//! While all device configuration can be created and handled via API calls, the library supports
75//! 100s of devices, meaning doing this in code would be rather unwieldy, and any new device
76//! additions would require library version revs. To allow for adding and updating configurations
77//! possibly without the need for library updates, we externalize this configuration to JSON files.
78//!
79//! Similarly, GUIs and other utilities have been created to facilitate creation of User
80//! Configurations, and these are also stored to files and loadable by the library.
81//!
82//! These files are handled in the [Device Configuration File Module in the Utils portion of the
83//! library](crate::util::device_configuration). More information on the file format and loading
84//! strategies can be found there.
85//!
86//! ## Architecture
87//!
88//! The [DeviceConfigurationManager] consists of a tree of types and usage flow that may be a bit
89//! confusing, so we'll outline and summarize them here.
90//!
91//! At the top level is the [DeviceConfigurationManager] itself. It contains 4 different pieces of
92//! information:
93//!
94//! - Protocol device specifiers and attributes
95//! - Factory/Builder instances for [ButtplugProtocols](crate::device::protocol::ButtplugProtocol)
96//! - User configuration information (allow/deny lists, per-device protocol attributes, etc...)
97//!
98//! The [DeviceConfigurationManager] is created when a ButtplugServer comes up, and which time
99//! protocols and user configurations can be added. After this, it is queried any time a new device
100//! is found, to see whether a registered protocol is usable with that device.
101//!
102//! ### Adding Protocols
103//!
104//! Adding protocols to the DCM happens via the add_protocol_factory and remove_protocol_factory
105//! methods.
106//!
107//! ### Protocol Device Specifiers
108//!
109//! In order to know if a discovered device can be used by Buttplug, it needs to be checked for
110//! identifying information. The library use "specifiers" (like [BluetoothLESpecifier],
111//! [USBSpecifier], etc...) for this. Specifiers contain device identification and connection
112//! information, and we compare groups of specifiers in protocol configurations (as part of the
113//! [ProtocolDeviceConfiguration] instance) with a specifier built from discovered devices to see if
114//! there are any matches.
115//!
116//! For instance, we know the Bluetooth LE information for WeVibe toys, all of which is stored with
117//! the WeVibe protocol configuration. The WeVibe protocol configuration has a Bluetooth LE
118//! specifier with all of that information. When someone has a, say, WeVibe Ditto, they can turn it
119//! on and put it into bluetooth discovery mode. If Buttplug is scanning for devices, we'll see the
120//! Ditto, via its corresponding Bluetooth advertisement. Data from this advertisement can be turned
121//! into a Bluetooth LE specifier. We can then match the specifier made from the advertisement
122//! against all the protocol specifiers in the system, and find that this device will work with the
123//! WeVibe protocol, at which point we'll move to the next step, protocol building.
124//!
125//! ### Protocol Building
126//!
127//! If a discovered device matches one or more protocol specifiers, a connection attempt begins,
128//! where each matched protocol is given a chance to see if it can identify and communicate with the
129//! device. If a protocol and device are matched, and connection is successful the initialized
130//! protocol instance is returned, and becomes part of the
131//! [ButtplugDevice](crate::device::ButtplugDevice) instance used by the
132//! [ButtplugServer](crate::server::ButtplugServer).
133//!
134//! ### User Configurations
135//!
136
137#[macro_use]
138extern crate strum_macros;
139
140#[macro_use]
141extern crate log;
142
143mod device_config_file;
144
145use buttplug_core::message::OutputType;
146pub use device_config_file::{load_protocol_configs, save_user_config};
147mod device_config_manager;
148pub use device_config_manager::*;
149mod specifier;
150pub use specifier::*;
151mod identifiers;
152pub use identifiers::*;
153mod device_definitions;
154pub use device_definitions::*;
155mod server_device_feature;
156pub use server_device_feature::*;
157mod endpoint;
158pub use endpoint::*;
159use uuid::Uuid;
160
161use thiserror::Error;
162
163#[derive(Error, Debug)]
164pub enum ButtplugDeviceConfigError {
165 /// Conversion to client type not possible with requested property type
166 #[error("Conversion of {0} to client type not possible with requested property type")]
167 InvalidOutputTypeConversion(String),
168 /// User set range exceeds bounds of possible configuration range
169 #[error("User set range exceeds bounds of possible configuration range")]
170 InvalidUserRange,
171 /// Base range required
172 #[error("Base range required for all feature outputs")]
173 BaseRangeRequired,
174 /// Base ID not found, cannot match user device/feature to a base device/feature
175 #[error("Device definition with base id {0} not found")]
176 BaseIdNotFound(Uuid),
177 #[error("Feature vectors between base and user device definitions do not match")]
178 UserFeatureMismatch,
179 #[error("Output value {0} not in range {1}")]
180 InvalidOutputValue(i32, String),
181 #[error("Output type {0} not available on device")]
182 InvalidOutput(OutputType),
183 #[error("Float value {0} is not 0 < x < 1")]
184 InvalidFloatConversion(f64),
185}