Skip to content
Merged
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 nmrs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
All notable changes to the `nmrs` crate will be documented in this file.

## [Unreleased]
### Changed
- Convert BDADDR to BlueZ device path via `bluez_device_path` helper ([#266](https://github.com/cachebag/nmrs/pull/266))

## [2.1.0] - 2026-02-28
### Added
Expand Down
36 changes: 15 additions & 21 deletions nmrs/src/core/bluetooth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::monitoring::bluetooth::Bluetooth;
use crate::monitoring::transport::ActiveTransport;
use crate::types::constants::device_state;
use crate::types::constants::device_type;
use crate::util::utils::bluez_device_path;
use crate::util::validation::validate_bluetooth_address;
use crate::ConnectionError;
use crate::{
Expand All @@ -45,10 +46,7 @@ pub(crate) async fn populate_bluez_info(
) -> Result<(Option<String>, Option<String>)> {
validate_bluetooth_address(bdaddr)?;

// [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
// This replaces ':' with '_' in the BDADDR to form the correct D-Bus object path.
// TODO: Instead of hardcoding hci0, we should determine the actual adapter name.
let bluez_path = format!("/org/bluez/hci0/dev_{}", bdaddr.replace(':', "_"));
let bluez_path = bluez_device_path(bdaddr);

match BluezDeviceExtProxy::builder(conn)
.path(bluez_path)?
Expand Down Expand Up @@ -144,14 +142,8 @@ pub(crate) async fn connect_bluetooth(
// Check for saved connection
let saved = get_saved_connection_path(conn, name).await?;

// For Bluetooth, the "specific_object" is the remote device's D-Bus path
// Format: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX
// TODO: Instead of hardcoding the hci0, we should use the actual hardware adapter name.
let specific_object = OwnedObjectPath::try_from(format!(
"/org/bluez/hci0/dev_{}",
settings.bdaddr.replace(':', "_")
))
.map_err(|e| ConnectionError::InvalidAddress(format!("Invalid BlueZ path: {}", e)))?;
let specific_object = OwnedObjectPath::try_from(bluez_device_path(&settings.bdaddr))
.map_err(|e| ConnectionError::InvalidAddress(format!("Invalid BlueZ path: {e}")))?;

match saved {
Some(saved_path) => {
Expand Down Expand Up @@ -247,24 +239,26 @@ mod tests {

#[test]
fn test_bluez_path_format() {
// Test that bdaddr format is converted correctly for D-Bus path
let bdaddr = "00:1A:7D:DA:71:13";
let expected_path = "/org/bluez/hci0/dev_00_1A_7D_DA_71_13";
let actual_path = format!("/org/bluez/hci0/dev_{}", bdaddr.replace(':', "_"));
assert_eq!(actual_path, expected_path);
assert_eq!(
bluez_device_path("00:1A:7D:DA:71:13"),
"/org/bluez/hci0/dev_00_1A_7D_DA_71_13"
);
}

#[test]
fn test_bluez_path_format_various_addresses() {
let test_cases = vec![
let test_cases = [
("AA:BB:CC:DD:EE:FF", "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF"),
("00:00:00:00:00:00", "/org/bluez/hci0/dev_00_00_00_00_00_00"),
("C8:1F:E8:F0:51:57", "/org/bluez/hci0/dev_C8_1F_E8_F0_51_57"),
];

for (bdaddr, expected_path) in test_cases {
let actual_path = format!("/org/bluez/hci0/dev_{}", bdaddr.replace(':', "_"));
assert_eq!(actual_path, expected_path, "Failed for bdaddr: {}", bdaddr);
for (bdaddr, expected) in test_cases {
assert_eq!(
bluez_device_path(bdaddr),
expected,
"Failed for bdaddr: {bdaddr}"
);
}
}

Expand Down
9 changes: 9 additions & 0 deletions nmrs/src/util/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ pub(crate) async fn extract_connection_state_reason(
}
}

/// Constructs a BlueZ D-Bus object path from a Bluetooth device address.
///
/// Converts a BDADDR like `"00:1A:7D:DA:71:13"` into
/// `"/org/bluez/hci0/dev_00_1A_7D_DA_71_13"`.
// TODO: Instead of hardcoding hci0, determine the actual adapter name.
pub(crate) fn bluez_device_path(bdaddr: &str) -> String {
format!("/org/bluez/hci0/dev_{}", bdaddr.replace(':', "_"))
}

/// Macro to convert Result to Option with error logging.
/// Usage: `try_log!(result, "context message")?`
#[macro_export]
Expand Down
Loading