Add some more cloud 3s packets
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
debug_println,
|
debug_println,
|
||||||
devices::{ChargingStatus, Device, DeviceError, DeviceEvent, DeviceState},
|
devices::{ChargingStatus, Color, Device, DeviceError, DeviceEvent, DeviceState},
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -34,27 +34,26 @@ const EQ_CMD: [u8; 5] = [0x02, 0x03, 0x00, 0x00, 0x5f];
|
|||||||
const EQ_PACKET_SIZE: usize = 64;
|
const EQ_PACKET_SIZE: usize = 64;
|
||||||
|
|
||||||
// Battery packet
|
// Battery packet
|
||||||
const BATTERY_PACKET: [u8; 64] = {
|
const BASE_PACKET: [u8; 62] = {
|
||||||
let mut packet = [0u8; 64];
|
let mut packet = [0u8; 62];
|
||||||
packet[0] = 0x0C;
|
packet[0] = 0x0C;
|
||||||
packet[1] = 0x02;
|
packet[1] = 0x02;
|
||||||
packet[2] = 0x03;
|
packet[2] = 0x03;
|
||||||
packet[3] = 0x01;
|
packet[3] = 0x01;
|
||||||
packet[4] = 0x00;
|
packet[4] = 0x00;
|
||||||
packet[5] = 0x06;
|
|
||||||
packet[6] = 0x00;
|
|
||||||
packet
|
packet
|
||||||
};
|
};
|
||||||
|
const RESPONSE_ID: u8 = 0x0C;
|
||||||
|
const NOTIFICATION_ID: u8 = 0x0D;
|
||||||
|
|
||||||
const CHARGING_RESPONSE: [u8; 64] = {
|
const BATTERY_COMMAND_ID: u8 = 0x06;
|
||||||
let mut packet = [0u8; 64];
|
const DONGLE_CONNECTED_COMMAND_ID: u8 = 0x02;
|
||||||
packet[0] = 0x0D;
|
const COLOR_COMMAND_ID: u8 = 0x4D;
|
||||||
packet[1] = 0x02;
|
const CHARGE_STATE_COMMAND_ID: u8 = 0x48;
|
||||||
packet[2] = 0x03;
|
const GET_MIC_MUTE_COMMAND_ID: u8 = 0x04;
|
||||||
packet[3] = 0x00;
|
const GET_SIDE_TONE_COMMAND_ID: u8 = 0x16;
|
||||||
packet[4] = 0x0A;
|
const GET_AUTO_POWER_OFF_COMMAND_ID: u8 = 0x4B;
|
||||||
packet
|
const GET_VOICE_PROMPT_COMMAND_ID: u8 = 0x14;
|
||||||
};
|
|
||||||
|
|
||||||
// Button report header (incoming from headset)
|
// Button report header (incoming from headset)
|
||||||
const CONSUMER_CONTROL_HEADER: u8 = 0x0f;
|
const CONSUMER_CONTROL_HEADER: u8 = 0x0f;
|
||||||
@@ -94,6 +93,49 @@ fn make_equalizer_band_packet(band_index: u8, db_value: f32) -> Vec<u8> {
|
|||||||
packet
|
packet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_automatic_shutdown_payload(high: u8, low: u8) -> Duration {
|
||||||
|
let num = (high as u64) * 256 + low as u64;
|
||||||
|
Duration::from_secs(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response(response: &[u8]) -> Option<Vec<DeviceEvent>> {
|
||||||
|
if response[6] == 0xFF {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match response[5] {
|
||||||
|
DONGLE_CONNECTED_COMMAND_ID => Some(vec![DeviceEvent::WirelessConnected(response[6] == 2)]),
|
||||||
|
GET_MIC_MUTE_COMMAND_ID => Some(vec![DeviceEvent::Muted(response[6] == 1)]),
|
||||||
|
BATTERY_COMMAND_ID => Some(vec![DeviceEvent::BatterLevel(response[6])]),
|
||||||
|
GET_VOICE_PROMPT_COMMAND_ID => Some(vec![DeviceEvent::VoicePrompt(response[6] == 1)]),
|
||||||
|
GET_SIDE_TONE_COMMAND_ID => Some(vec![DeviceEvent::SideToneOn(response[6] == 1)]),
|
||||||
|
CHARGE_STATE_COMMAND_ID => Some(vec![DeviceEvent::Charging(ChargingStatus::from(
|
||||||
|
response[6],
|
||||||
|
))]),
|
||||||
|
GET_AUTO_POWER_OFF_COMMAND_ID => Some(vec![DeviceEvent::AutomaticShutdownAfter(
|
||||||
|
parse_automatic_shutdown_payload(response[6], response[7]),
|
||||||
|
)]),
|
||||||
|
COLOR_COMMAND_ID => Some(vec![DeviceEvent::ProductColor(Color::from(response[6]))]),
|
||||||
|
3 | 5 => None,
|
||||||
|
_ => {
|
||||||
|
debug_println!("Unknown response {:?}", response);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_notification(response: &[u8]) -> Option<Vec<DeviceEvent>> {
|
||||||
|
match response[4] {
|
||||||
|
1 => Some(vec![DeviceEvent::BatterLevel(response[5])]),
|
||||||
|
3 => Some(vec![DeviceEvent::Muted(response[5] == 1)]),
|
||||||
|
5 => Some(vec![DeviceEvent::SideToneOn(response[5] == 1)]),
|
||||||
|
10 => Some(vec![DeviceEvent::Charging(ChargingStatus::from(
|
||||||
|
response[5],
|
||||||
|
))]),
|
||||||
|
12 => Some(vec![DeviceEvent::WirelessConnected(response[5] == 1)]),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CloudIIISWireless {
|
pub struct CloudIIISWireless {
|
||||||
state: DeviceState,
|
state: DeviceState,
|
||||||
}
|
}
|
||||||
@@ -110,13 +152,16 @@ impl CloudIIISWireless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Device for CloudIIISWireless {
|
impl Device for CloudIIISWireless {
|
||||||
// Cloud III S: Battery query not discovered yet
|
|
||||||
fn get_charging_packet(&self) -> Option<Vec<u8>> {
|
fn get_charging_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = CHARGE_STATE_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_battery_packet(&self) -> Option<Vec<u8>> {
|
fn get_battery_packet(&self) -> Option<Vec<u8>> {
|
||||||
Some(BATTERY_PACKET.to_vec())
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = BATTERY_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloud III S: Auto shutdown via SET_REPORT (report ID 0x0c)
|
// Cloud III S: Auto shutdown via SET_REPORT (report ID 0x0c)
|
||||||
@@ -126,13 +171,15 @@ impl Device for CloudIIISWireless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_automatic_shut_down_packet(&self) -> Option<Vec<u8>> {
|
fn get_automatic_shut_down_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = GET_AUTO_POWER_OFF_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloud III S: Cannot query mic state (no response received)
|
|
||||||
// Physical mic button doesn't emit state packets over HID
|
|
||||||
fn get_mute_packet(&self) -> Option<Vec<u8>> {
|
fn get_mute_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = GET_MIC_MUTE_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloud III S: Mic control - CONFIRMED WORKING
|
// Cloud III S: Mic control - CONFIRMED WORKING
|
||||||
@@ -157,12 +204,15 @@ impl Device for CloudIIISWireless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_product_color_packet(&self) -> Option<Vec<u8>> {
|
fn get_product_color_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = COLOR_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloud III S: Sidetone not discovered yet
|
|
||||||
fn get_side_tone_packet(&self) -> Option<Vec<u8>> {
|
fn get_side_tone_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = GET_SIDE_TONE_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_side_tone_packet(&self, _side_tone_on: bool) -> Option<Vec<u8>> {
|
fn set_side_tone_packet(&self, _side_tone_on: bool) -> Option<Vec<u8>> {
|
||||||
@@ -178,7 +228,9 @@ impl Device for CloudIIISWireless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_voice_prompt_packet(&self) -> Option<Vec<u8>> {
|
fn get_voice_prompt_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = GET_VOICE_PROMPT_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_voice_prompt_packet(&self, _enable: bool) -> Option<Vec<u8>> {
|
fn set_voice_prompt_packet(&self, _enable: bool) -> Option<Vec<u8>> {
|
||||||
@@ -186,7 +238,9 @@ impl Device for CloudIIISWireless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_wireless_connected_status_packet(&self) -> Option<Vec<u8>> {
|
fn get_wireless_connected_status_packet(&self) -> Option<Vec<u8>> {
|
||||||
None
|
let mut packet = BASE_PACKET.to_vec();
|
||||||
|
packet[5] = DONGLE_CONNECTED_COMMAND_ID;
|
||||||
|
Some(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sirk_packet(&self) -> Option<Vec<u8>> {
|
fn get_sirk_packet(&self) -> Option<Vec<u8>> {
|
||||||
@@ -231,24 +285,8 @@ impl Device for CloudIIISWireless {
|
|||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
cmd_id if cmd_id == BATTERY_PACKET[0] => {
|
RESPONSE_ID => parse_response(response),
|
||||||
if BATTERY_PACKET[0..=5] == response[0..=5] {
|
NOTIFICATION_ID => parse_notification(response),
|
||||||
Some(vec![DeviceEvent::BatterLevel(response[6])])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd_id if cmd_id == CHARGING_RESPONSE[0] => {
|
|
||||||
if CHARGING_RESPONSE[0..=4] == response[0..=4] {
|
|
||||||
if response[5] == 1 {
|
|
||||||
Some(vec![DeviceEvent::Charging(ChargingStatus::Charging)])
|
|
||||||
} else {
|
|
||||||
Some(vec![DeviceEvent::Charging(ChargingStatus::NotCharging)])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
debug_println!("Unknown device event: {:?}", response);
|
debug_println!("Unknown device event: {:?}", response);
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ pub enum DeviceEvent {
|
|||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
BlackBlack,
|
BlackBlack,
|
||||||
|
WhiteWhite,
|
||||||
BlackRed,
|
BlackRed,
|
||||||
UnknownColor(u8),
|
UnknownColor(u8),
|
||||||
}
|
}
|
||||||
@@ -366,6 +367,7 @@ impl Display for Color {
|
|||||||
"{}",
|
"{}",
|
||||||
match self {
|
match self {
|
||||||
Color::BlackBlack => "Black".to_string(),
|
Color::BlackBlack => "Black".to_string(),
|
||||||
|
Color::WhiteWhite => "White".to_string(),
|
||||||
Color::BlackRed => "Red".to_string(),
|
Color::BlackRed => "Red".to_string(),
|
||||||
Color::UnknownColor(n) => format!("Unknown color {}", n),
|
Color::UnknownColor(n) => format!("Unknown color {}", n),
|
||||||
}
|
}
|
||||||
@@ -377,6 +379,7 @@ impl From<u8> for Color {
|
|||||||
fn from(color: u8) -> Self {
|
fn from(color: u8) -> Self {
|
||||||
match color {
|
match color {
|
||||||
0 => Color::BlackBlack,
|
0 => Color::BlackBlack,
|
||||||
|
1 => Color::WhiteWhite,
|
||||||
2 => Color::BlackRed,
|
2 => Color::BlackRed,
|
||||||
_ => Color::UnknownColor(color),
|
_ => Color::UnknownColor(color),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user