Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/buttplug_server/src/device/protocol_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub mod thehandy_v3;
pub mod tryfun;
pub mod tryfun_blackhole;
pub mod tryfun_meta2;
pub mod utimi;
pub mod vibcrafter;
pub mod vibratissimo;
pub mod vorze_sa;
Expand Down Expand Up @@ -538,6 +539,7 @@ pub fn get_default_protocol_map() -> HashMap<String, Arc<dyn ProtocolIdentifierF
&mut map,
tcode_v03::setup::TCodeV03IdentifierFactory::default(),
);
add_to_protocol_map(&mut map, utimi::setup::UtimiIdentifierFactory::default());
add_to_protocol_map(
&mut map,
vibcrafter::setup::VibCrafterIdentifierFactory::default(),
Expand Down
108 changes: 108 additions & 0 deletions crates/buttplug_server/src/device/protocol_impl/utimi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Buttplug Rust Source Code File - See https://buttplug.io for more info.
//
// Copyright 2016-2026 Nonpolynomial Labs LLC. All rights reserved.
//
// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
// for full license information.

use crate::device::hardware::{HardwareCommand, HardwareWriteCmd};
use crate::device::{
hardware::Hardware,
protocol::{
ProtocolHandler,
ProtocolIdentifier,
ProtocolInitializer,
generic_protocol_initializer_setup,
},
};
use async_trait::async_trait;
use buttplug_core::errors::ButtplugDeviceError;
use buttplug_server_device_config::{
Endpoint,
ProtocolCommunicationSpecifier,
ServerDeviceDefinition,
UserDeviceIdentifier,
};
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering};
use uuid::{Uuid, uuid};

const UTIMI_PROTOCOL_UUID: Uuid = uuid!("d4a3e2b1-7c56-4f89-a012-3b4c5d6e7f80");

generic_protocol_initializer_setup!(Utimi, "utimi");

#[derive(Default)]
pub struct UtimiInitializer {}

#[async_trait]
impl ProtocolInitializer for UtimiInitializer {
async fn initialize(
&mut self,
_hardware: Arc<Hardware>,
_def: &ServerDeviceDefinition,
) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
Ok(Arc::new(Utimi::default()))
}
}

pub struct Utimi {
// Benben lightspot protocol supports up to 5 motor slots.
// Utimi devices use slot 0 (vibrate) and slot 1 (thrust/oscillate).
last_cmds: [AtomicU8; 5],
}

impl Default for Utimi {
fn default() -> Self {
Self {
last_cmds: [const { AtomicU8::new(0) }; 5],
}
}
}

impl Utimi {
fn send_command(
&self,
index: u32,
speed: u32,
) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
self.last_cmds[index as usize].store(speed as u8, Ordering::Relaxed);
Ok(vec![
HardwareWriteCmd::new(
&[UTIMI_PROTOCOL_UUID],
Endpoint::Tx,
vec![
0xa0,
0x03,
self.last_cmds[0].load(Ordering::Relaxed),
self.last_cmds[1].load(Ordering::Relaxed),
self.last_cmds[2].load(Ordering::Relaxed),
self.last_cmds[3].load(Ordering::Relaxed),
self.last_cmds[4].load(Ordering::Relaxed),
0xaa,
],
false,
)
.into(),
])
}
}

impl ProtocolHandler for Utimi {
fn handle_output_vibrate_cmd(
&self,
feature_index: u32,
_feature_id: Uuid,
speed: u32,
) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
self.send_command(feature_index, speed)
}

fn handle_output_oscillate_cmd(
&self,
feature_index: u32,
_feature_id: Uuid,
speed: u32,
) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
self.send_command(feature_index, speed)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
defaults:
name: Utimi Device
features:
- id: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
output:
vibrate:
value:
- 0
- 255
index: 0
- id: b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e
output:
oscillate:
value:
- 0
- 255
index: 1
id: c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f
configurations:
- identifier:
- Utimi
name: Utimi Prostate Massager
id: d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80
communication:
- btle:
names:
- Utimi*
services:
0000ffa0-0000-1000-8000-00805f9b34fb:
tx: 0000ffa1-0000-1000-8000-00805f9b34fb