use super::{ButtplugDeviceResultFuture, ButtplugProtocol, ButtplugProtocolCommandHandler};
use crate::{
core::errors::ButtplugDeviceError,
device::{ButtplugDeviceEvent, DeviceSubscribeCmd},
};
use crate::{
core::{
errors::ButtplugError,
messages::{self, ButtplugDeviceCommandMessageUnion, MessageAttributesMap},
},
device::{
protocol::{generic_command_manager::GenericCommandManager, ButtplugProtocolProperties},
DeviceImpl,
DeviceWriteCmd,
Endpoint,
},
};
use async_mutex::Mutex;
use futures::future::BoxFuture;
use futures::StreamExt;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
#[derive(ButtplugProtocolProperties)]
pub struct Lovense {
name: String,
message_attributes: MessageAttributesMap,
manager: Arc<Mutex<GenericCommandManager>>,
stop_commands: Vec<ButtplugDeviceCommandMessageUnion>,
rotation_direction: Arc<AtomicBool>,
}
impl ButtplugProtocol for Lovense {
fn new_protocol(name: &str, attrs: MessageAttributesMap) -> Box<dyn ButtplugProtocol> {
let manager = GenericCommandManager::new(&attrs);
Box::new(Self {
name: name.to_owned(),
message_attributes: attrs,
stop_commands: manager.get_stop_commands(),
manager: Arc::new(Mutex::new(manager)),
rotation_direction: Arc::new(AtomicBool::new(false)),
})
}
fn initialize(
device_impl: &dyn DeviceImpl,
) -> BoxFuture<'static, Result<Option<String>, ButtplugError>> {
let subscribe_fut = device_impl.subscribe(DeviceSubscribeCmd::new(Endpoint::Rx));
let msg = DeviceWriteCmd::new(Endpoint::Tx, b"DeviceType;".to_vec(), false);
let info_fut = device_impl.write_value(msg);
let mut event_receiver = device_impl.get_event_receiver();
Box::pin(async move {
let identifier;
subscribe_fut.await?;
info_fut.await?;
match event_receiver.next().await {
Some(ButtplugDeviceEvent::Notification(_, n)) => {
let type_response = std::str::from_utf8(&n).unwrap().to_owned();
info!("Lovense Device Type Response: {}", type_response);
identifier = type_response.split(':').collect::<Vec<&str>>()[0].to_owned();
}
Some(ButtplugDeviceEvent::Removed) => {
return Err(
ButtplugDeviceError::ProtocolSpecificError(
"Lovense",
"Lovense Device disconnected while getting DeviceType info.",
)
.into(),
);
}
None => {
return Err(
ButtplugDeviceError::ProtocolSpecificError(
"Lovense",
"Did not get DeviceType return from Lovense device in time",
)
.into(),
);
}
};
Ok(Some(identifier))
})
}
}
impl ButtplugProtocolCommandHandler for Lovense {
fn handle_vibrate_cmd(
&self,
device: Arc<Box<dyn DeviceImpl>>,
msg: messages::VibrateCmd,
) -> ButtplugDeviceResultFuture {
let manager = self.manager.clone();
Box::pin(async move {
let result = manager.lock().await.update_vibration(&msg, false)?;
let mut fut_vec = vec![];
if let Some(cmds) = result {
if cmds[0].is_some() && (cmds.len() == 1 || cmds.windows(2).all(|w| w[0] == w[1])) {
let lovense_cmd = format!("Vibrate:{};", cmds[0].unwrap()).as_bytes().to_vec();
let fut = device.write_value(DeviceWriteCmd::new(Endpoint::Tx, lovense_cmd, false));
fut.await?;
return Ok(messages::Ok::default().into());
}
for (i, cmd) in cmds.iter().enumerate() {
if let Some(speed) = cmd {
let lovense_cmd = format!("Vibrate{}:{};", i + 1, speed).as_bytes().to_vec();
fut_vec.push(device.write_value(DeviceWriteCmd::new(Endpoint::Tx, lovense_cmd, false)));
}
}
}
for fut in fut_vec {
fut.await?;
}
Ok(messages::Ok::default().into())
})
}
fn handle_rotate_cmd(
&self,
device: Arc<Box<dyn DeviceImpl>>,
msg: messages::RotateCmd,
) -> ButtplugDeviceResultFuture {
let manager = self.manager.clone();
let direction = self.rotation_direction.clone();
Box::pin(async move {
let result = manager.lock().await.update_rotation(&msg)?;
if let Some((speed, clockwise)) = result[0] {
let lovense_cmd = format!("Rotate:{};", speed).as_bytes().to_vec();
let fut = device.write_value(DeviceWriteCmd::new(Endpoint::Tx, lovense_cmd, false));
fut.await?;
let dir = direction.load(Ordering::SeqCst);
if dir != clockwise {
direction.store(clockwise, Ordering::SeqCst);
let fut = device.write_value(DeviceWriteCmd::new(
Endpoint::Tx,
b"RotateChange;".to_vec(),
false,
));
fut.await?;
}
}
Ok(messages::Ok::default().into())
})
}
fn handle_battery_level_cmd(
&self,
device: Arc<Box<dyn DeviceImpl>>,
message: messages::BatteryLevelCmd,
) -> ButtplugDeviceResultFuture {
let mut device_notification_receiver = device.get_event_receiver();
Box::pin(async move {
let write_fut = device.write_value(DeviceWriteCmd::new(
Endpoint::Tx,
b"Battery;".to_vec(),
false,
));
write_fut.await?;
while let Some(event) = device_notification_receiver.recv().await {
match event {
ButtplugDeviceEvent::Notification(_, data) => {
if let Ok(data_str) = std::str::from_utf8(&data) {
let len = data_str.len();
if let Ok(level) = data_str[0..(len - 1)].parse::<u8>() {
return Ok(
messages::BatteryLevelReading::new(message.device_index, level as f64 / 100f64)
.into(),
);
}
}
}
ButtplugDeviceEvent::Removed => {
debug!("Lovense device removed, exiting notification loop.");
return Err(
ButtplugDeviceError::DeviceNotConnected(
"Device disconnected while waiting for battery message.".to_owned(),
)
.into(),
);
}
}
}
Err(
ButtplugDeviceError::DeviceNotConnected(
"Device disconnected while waiting for battery message.".to_owned(),
)
.into(),
)
})
}
}