linux-rust: add skeleton for other devices

This commit is contained in:
Kavish Devar
2025-11-07 01:57:14 +05:30
parent 934df2419a
commit a2cda688d4
20 changed files with 1449 additions and 338 deletions

View File

@@ -0,0 +1,269 @@
use crate::bluetooth::aacp::{AACPManager, ProximityKeyType, AACPEvent, AirPodsLEKeys};
use crate::bluetooth::aacp::ControlCommandIdentifiers;
// use crate::bluetooth::att::ATTManager;
use crate::media_controller::MediaController;
use bluer::Address;
use log::{debug, info, error};
use std::sync::Arc;
use ksni::Handle;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use tokio::time::{sleep, Duration};
use crate::ui::tray::MyTray;
use crate::ui::messages::BluetoothUIMessage;
pub struct AirPodsDevice {
pub mac_address: Address,
pub aacp_manager: AACPManager,
// pub att_manager: ATTManager,
pub media_controller: Arc<Mutex<MediaController>>,
// pub command_tx: Option<tokio::sync::mpsc::UnboundedSender<(ControlCommandIdentifiers, Vec<u8>)>>,
}
impl AirPodsDevice {
pub async fn new(
mac_address: Address,
tray_handle: Option<Handle<MyTray>>,
ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage>,
) -> Self {
info!("Creating new AirPodsDevice for {}", mac_address);
let mut aacp_manager = AACPManager::new();
aacp_manager.connect(mac_address).await;
// let mut att_manager = ATTManager::new();
// att_manager.connect(mac_address).await.expect("Failed to connect ATT");
if let Some(handle) = &tray_handle {
handle.update(|tray: &mut MyTray| tray.connected = true).await;
}
info!("Sending handshake");
if let Err(e) = aacp_manager.send_handshake().await {
error!("Failed to send handshake to AirPods device: {}", e);
}
sleep(Duration::from_millis(100)).await;
info!("Setting feature flags");
if let Err(e) = aacp_manager.send_set_feature_flags_packet().await {
error!("Failed to set feature flags: {}", e);
}
sleep(Duration::from_millis(100)).await;
info!("Requesting notifications");
if let Err(e) = aacp_manager.send_notification_request().await {
error!("Failed to request notifications: {}", e);
}
info!("sending some packet");
if let Err(e) = aacp_manager.send_some_packet().await {
error!("Failed to send some packet: {}", e);
}
info!("Requesting Proximity Keys: IRK and ENC_KEY");
if let Err(e) = aacp_manager.send_proximity_keys_request(
vec![ProximityKeyType::Irk, ProximityKeyType::EncKey],
).await {
error!("Failed to request proximity keys: {}", e);
}
let session = bluer::Session::new().await.expect("Failed to get bluer session");
let adapter = session.default_adapter().await.expect("Failed to get default adapter");
let local_mac = adapter.address().await.expect("Failed to get adapter address").to_string();
let media_controller = Arc::new(Mutex::new(MediaController::new(mac_address.to_string(), local_mac.clone())));
let mc_clone = media_controller.clone();
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
let (command_tx, mut command_rx) = tokio::sync::mpsc::unbounded_channel();
aacp_manager.set_event_channel(tx).await;
if let Some(handle) = &tray_handle {
handle.update(|tray: &mut MyTray| tray.command_tx = Some(command_tx.clone())).await;
}
let aacp_manager_clone = aacp_manager.clone();
tokio::spawn(async move {
while let Some((id, value)) = command_rx.recv().await {
if let Err(e) = aacp_manager_clone.send_control_command(id, &value).await {
log::error!("Failed to send control command: {}", e);
}
}
});
let mc_listener = media_controller.lock().await;
let aacp_manager_clone_listener = aacp_manager.clone();
mc_listener.start_playback_listener(aacp_manager_clone_listener, command_tx.clone()).await;
drop(mc_listener);
let (listening_mode_tx, mut listening_mode_rx) = tokio::sync::mpsc::unbounded_channel();
aacp_manager.subscribe_to_control_command(ControlCommandIdentifiers::ListeningMode, listening_mode_tx).await;
let tray_handle_clone = tray_handle.clone();
tokio::spawn(async move {
while let Some(value) = listening_mode_rx.recv().await {
if let Some(handle) = &tray_handle_clone {
handle.update(|tray: &mut MyTray| {
tray.listening_mode = Some(value[0]);
}).await;
}
}
});
let (allow_off_tx, mut allow_off_rx) = tokio::sync::mpsc::unbounded_channel();
aacp_manager.subscribe_to_control_command(ControlCommandIdentifiers::AllowOffOption, allow_off_tx).await;
let tray_handle_clone = tray_handle.clone();
tokio::spawn(async move {
while let Some(value) = allow_off_rx.recv().await {
if let Some(handle) = &tray_handle_clone {
handle.update(|tray: &mut MyTray| {
tray.allow_off_option = Some(value[0]);
}).await;
}
}
});
let (conversation_detect_tx, mut conversation_detect_rx) = tokio::sync::mpsc::unbounded_channel();
aacp_manager.subscribe_to_control_command(ControlCommandIdentifiers::ConversationDetectConfig, conversation_detect_tx).await;
let tray_handle_clone = tray_handle.clone();
tokio::spawn(async move {
while let Some(value) = conversation_detect_rx.recv().await {
if let Some(handle) = &tray_handle_clone {
handle.update(|tray: &mut MyTray| {
tray.conversation_detect_enabled = Some(value[0] == 0x01);
}).await;
}
}
});
let (owns_connection_tx, mut owns_connection_rx) = tokio::sync::mpsc::unbounded_channel();
aacp_manager.subscribe_to_control_command(ControlCommandIdentifiers::OwnsConnection, owns_connection_tx).await;
let mc_clone_owns = media_controller.clone();
tokio::spawn(async move {
while let Some(value) = owns_connection_rx.recv().await {
let owns = value.get(0).copied().unwrap_or(0) != 0;
if !owns {
info!("Lost ownership, pausing media and disconnecting audio");
let controller = mc_clone_owns.lock().await;
controller.pause_all_media().await;
controller.deactivate_a2dp_profile().await;
}
}
});
let aacp_manager_clone_events = aacp_manager.clone();
let local_mac_events = local_mac.clone();
let ui_tx_clone = ui_tx.clone();
let command_tx_clone = command_tx.clone();
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
let event_clone = event.clone();
match event {
AACPEvent::EarDetection(old_status, new_status) => {
debug!("Received EarDetection event: old_status={:?}, new_status={:?}", old_status, new_status);
let controller = mc_clone.lock().await;
debug!("Calling handle_ear_detection with old_status: {:?}, new_status: {:?}", old_status, new_status);
controller.handle_ear_detection(old_status, new_status).await;
}
AACPEvent::BatteryInfo(battery_info) => {
debug!("Received BatteryInfo event: {:?}", battery_info);
if let Some(handle) = &tray_handle {
handle.update(|tray: &mut MyTray| {
for b in &battery_info {
match b.component as u8 {
0x02 => {
tray.battery_r = Some(b.level);
tray.battery_r_status = Some(b.status);
}
0x04 => {
tray.battery_l = Some(b.level);
tray.battery_l_status = Some(b.status);
}
0x08 => {
tray.battery_c = Some(b.level);
tray.battery_c_status = Some(b.status);
}
_ => {}
}
}
}).await;
}
debug!("Updated tray with new battery info");
let _ = ui_tx_clone.send(BluetoothUIMessage::AACPUIEvent(mac_address.to_string(), event_clone));
debug!("Sent BatteryInfo event to UI");
}
AACPEvent::ControlCommand(status) => {
debug!("Received ControlCommand event: {:?}", status);
let _ = ui_tx_clone.send(BluetoothUIMessage::AACPUIEvent(mac_address.to_string(), event_clone));
debug!("Sent ControlCommand event to UI");
}
AACPEvent::ConversationalAwareness(status) => {
debug!("Received ConversationalAwareness event: {}", status);
let controller = mc_clone.lock().await;
controller.handle_conversational_awareness(status).await;
}
AACPEvent::ConnectedDevices(old_devices, new_devices) => {
let local_mac = local_mac_events.clone();
let new_devices_filtered = new_devices.iter().filter(|new_device| {
let not_in_old = old_devices.iter().all(|old_device| old_device.mac != new_device.mac);
let not_local = new_device.mac != local_mac;
not_in_old && not_local
});
for device in new_devices_filtered {
info!("New connected device: {}, info1: {}, info2: {}", device.mac, device.info1, device.info2);
info!("Sending new Tipi packet for device {}, and sending media info to the device", device.mac);
let aacp_manager_clone = aacp_manager_clone_events.clone();
let local_mac_clone = local_mac.clone();
let device_mac_clone = device.mac.clone();
tokio::spawn(async move {
if let Err(e) = aacp_manager_clone.send_media_information_new_device(&local_mac_clone, &device_mac_clone).await {
error!("Failed to send media info new device: {}", e);
}
if let Err(e) = aacp_manager_clone.send_add_tipi_device(&local_mac_clone, &device_mac_clone).await {
error!("Failed to send add tipi device: {}", e);
}
});
}
}
AACPEvent::OwnershipToFalseRequest => {
info!("Received ownership to false request. Setting ownership to false and pausing media.");
let _ = command_tx_clone.send((ControlCommandIdentifiers::OwnsConnection, vec![0x00]));
let controller = mc_clone.lock().await;
controller.pause_all_media().await;
controller.deactivate_a2dp_profile().await;
}
_ => {
debug!("Received unhandled AACP event: {:?}", event);
let _ = ui_tx_clone.send(BluetoothUIMessage::AACPUIEvent(mac_address.to_string(), event_clone));
debug!("Sent unhandled AACP event to UI");
}
}
}
});
AirPodsDevice {
mac_address,
aacp_manager,
// att_manager,
media_controller,
// command_tx: Some(command_tx.clone()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AirPodsInformation {
pub name: String,
pub model_number: String,
pub manufacturer: String,
pub serial_number: String,
pub version1: String,
pub version2: String,
pub hardware_revision: String,
pub updater_identifier: String,
pub left_serial_number: String,
pub right_serial_number: String,
pub version3: String,
pub le_keys: AirPodsLEKeys
}

View File

@@ -0,0 +1,107 @@
use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::devices::airpods::AirPodsInformation;
use crate::devices::nothing::NothingInformation;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(PartialEq)]
pub enum DeviceType {
AirPods,
Nothing
}
impl Display for DeviceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeviceType::AirPods => write!(f, "AirPods"),
DeviceType::Nothing => write!(f, "Nothing"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", content = "data")]
pub enum DeviceInformation {
AirPods(AirPodsInformation),
Nothing(NothingInformation)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceData {
pub name: String,
pub type_: DeviceType,
pub information: Option<DeviceInformation>,
}
#[derive(Clone, Debug)]
pub enum DeviceState {
AirPods(AirPodsState),
Nothing(NothingState),
}
impl Display for DeviceState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeviceState::AirPods(_) => write!(f, "AirPods State"),
DeviceState::Nothing(_) => write!(f, "Nothing State"),
}
}
}
#[derive(Clone, Debug)]
pub struct AirPodsState {
pub conversation_awareness_enabled: bool,
}
#[derive(Clone, Debug)]
pub struct NothingState {
pub anc_mode: NothingAncMode,
}
#[derive(Clone, Debug)]
pub enum NothingAncMode {
Off,
LowNoiseCancellation,
MidNoiseCancellation,
HighNoiseCancellation,
AdaptiveNoiseCancellation,
Transparency
}
impl Display for NothingAncMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NothingAncMode::Off => write!(f, "Off"),
NothingAncMode::LowNoiseCancellation => write!(f, "Low Noise Cancellation"),
NothingAncMode::MidNoiseCancellation => write!(f, "Mid Noise Cancellation"),
NothingAncMode::HighNoiseCancellation => write!(f, "High Noise Cancellation"),
NothingAncMode::AdaptiveNoiseCancellation => write!(f, "Adaptive Noise Cancellation"),
NothingAncMode::Transparency => write!(f, "Transparency"),
}
}
}
impl NothingAncMode {
pub fn from_byte(value: u8) -> Self {
match value {
0x03 => NothingAncMode::LowNoiseCancellation,
0x02 => NothingAncMode::MidNoiseCancellation,
0x01 => NothingAncMode::HighNoiseCancellation,
0x04 => NothingAncMode::AdaptiveNoiseCancellation,
0x07 => NothingAncMode::Transparency,
0x05 => NothingAncMode::Off,
_ => NothingAncMode::Off,
}
}
pub fn to_byte(&self) -> u8 {
match self {
NothingAncMode::LowNoiseCancellation => 0x03,
NothingAncMode::MidNoiseCancellation => 0x02,
NothingAncMode::HighNoiseCancellation => 0x01,
NothingAncMode::AdaptiveNoiseCancellation => 0x04,
NothingAncMode::Transparency => 0x07,
NothingAncMode::Off => 0x05,
}
}
}

View File

@@ -0,0 +1,3 @@
pub mod airpods;
pub mod enums;
pub(crate) mod nothing;

View File

@@ -0,0 +1,167 @@
use std::collections::HashMap;
use std::time::Duration;
use bluer::Address;
use log::{debug, info};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use tokio::time::sleep;
use crate::bluetooth::att::{ATTHandles, ATTManager};
use crate::devices::enums::{DeviceData, DeviceInformation, DeviceType};
use crate::ui::messages::BluetoothUIMessage;
use crate::utils::get_devices_path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NothingInformation{
pub serial_number: String,
pub firmware_version: String
}
pub struct NothingDevice{
pub att_manager: ATTManager,
pub information: NothingInformation
}
impl NothingDevice{
pub async fn new(
mac_address: Address,
ui_tx: mpsc::UnboundedSender<BluetoothUIMessage>
) -> Self {
let mut att_manager = ATTManager::new();
att_manager.connect(mac_address).await.expect("Failed to connect");
let (tx, mut rx) = mpsc::unbounded_channel::<Vec<u8>>();
att_manager.register_listener(
ATTHandles::NothingEverythingRead,
tx
).await;
let devices: HashMap<String, DeviceData> =
std::fs::read_to_string(get_devices_path())
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default();
let device_key = mac_address.to_string();
let information = if let Some(device_data) = devices.get(&device_key) {
let info = device_data.information.clone();
if let Some(DeviceInformation::Nothing(ref nothing_info)) = info {
nothing_info.clone()
} else {
NothingInformation{
serial_number: String::new(),
firmware_version: String::new()
}
}
} else {
NothingInformation{
serial_number: String::new(),
firmware_version: String::new()
}
};
// Request version information
att_manager.write(
ATTHandles::NothingEverything,
&[
0x55, 0x20,
0x01, 0x42,
0xC0, 0x00,
0x00, 0x00,
0x00, 0x00 // something, idk
]
).await.expect("Failed to write");
sleep(Duration::from_millis(100)).await;
// Request serial number
att_manager.write(
ATTHandles::NothingEverything,
&[
0x55, 0x20,
0x01, 0x06,
0xC0, 0x00,
0x00, 0x13,
0x00, 0x00
]
).await.expect("Failed to write");
// let ui_tx_clone = ui_tx.clone();
let information_l = information.clone();
tokio::spawn(async move {
while let Some(data) = rx.recv().await {
if data.starts_with(&[
0x55, 0x20,
0x01, 0x42, 0x40
]) {
let firmware_version = String::from_utf8_lossy(&data[8..]).to_string();
info!("Received firmware version from Nothing device {}: {}", mac_address, firmware_version);
let new_information = NothingInformation{
serial_number: information_l.serial_number.clone(),
firmware_version: firmware_version.clone()
};
let mut new_devices = devices.clone();
new_devices.insert(
device_key.clone(),
DeviceData{
name: devices.get(&device_key)
.map(|d| d.name.clone())
.unwrap_or("Nothing Device".to_string()),
type_: devices.get(&device_key)
.map(|d| d.type_.clone())
.unwrap_or(DeviceType::Nothing),
information: Some(DeviceInformation::Nothing(new_information)),
}
);
let json = serde_json::to_string(&new_devices).unwrap();
std::fs::write(get_devices_path(), json).expect("Failed to write devices file");
} else if data.starts_with(
&[
0x55, 0x20,
0x01, 0x06, 0x40
]
) {
let serial_number_start_position = data.iter().position(|&b| b == "S".as_bytes()[0]).unwrap_or(8);
let serial_number_end = data.iter()
.skip(serial_number_start_position)
.position(|&b| b == 0x0A)
.map(|pos| pos + serial_number_start_position)
.unwrap_or(data.len());
if data.get(serial_number_start_position + 1) == Some(&"H".as_bytes()[0]) {
let serial_number = String::from_utf8_lossy(
&data[serial_number_start_position..serial_number_end]
).to_string();
info!("Received serial number from Nothing device {}: {}", mac_address, serial_number);
let new_information = NothingInformation{
serial_number: serial_number.clone(),
firmware_version: information_l.firmware_version.clone()
};
let mut new_devices = devices.clone();
new_devices.insert(
device_key.clone(),
DeviceData{
name: devices.get(&device_key)
.map(|d| d.name.clone())
.unwrap_or("Nothing Device".to_string()),
type_: devices.get(&device_key)
.map(|d| d.type_.clone())
.unwrap_or(DeviceType::Nothing),
information: Some(DeviceInformation::Nothing(new_information)),
}
);
let json = serde_json::to_string(&new_devices).unwrap();
std::fs::write(get_devices_path(), json).expect("Failed to write devices file");
} else {
debug!("Serial number format unexpected from Nothing device {}: {:?}", mac_address, data);
}
}
else {}
debug!("Received data from (Nothing) device {}, data: {:?}", mac_address, data);
}
});
NothingDevice{
att_manager,
information
}
}
}