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}