Skip to content

Support custom BLE device name discovery #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

weberval
Copy link

@weberval weberval commented Jul 20, 2025

Fixes #56

Summary by Sourcery

Allow customizing the BLE device name used for discovery by adding a new CLI flag and updating the BLE enumeration API to accept and use the provided name.

New Features:

  • Add a --device_name CLI option to specify the target BLE device name during discovery

Enhancements:

  • Update BleDevice::enumerate, enumerate_duration, from_peripheral, and single to accept a device_name parameter and filter on it
  • Pass the user-provided device_name through write_payload into BLE operations

Copy link

sourcery-ai bot commented Jul 20, 2025

Reviewer's Guide

This PR introduces a configurable BLE device name by adding a CLI flag and refactoring the BLE scanning and connection APIs to accept a dynamic name instead of a hardcoded constant. The device_name parameter is threaded through enumeration, filtering, and payload writing paths.

Sequence diagram for BLE device discovery with custom device name

sequenceDiagram
    actor User
    participant CLI as CLI
    participant Main as main.rs
    participant Device as Device (ble.rs)
    User->>CLI: Run command with --device_name
    CLI->>Main: Parse Args (device_name)
    Main->>Device: Device::single(device_name)
    Device->>Device: enumerate(device_name)
    Device->>Device: enumerate_duration(duration, device_name)
    Device->>Device: from_peripheral(peripheral, device_name)
    Device-->>Main: Return matching device
    Main-->>CLI: Continue with device operations
Loading

Class diagram for updated Device BLE API

classDiagram
    class Device {
        +async fn enumerate(device_name: &str) -> Result<Vec<Self>>
        +async fn enumerate_duration(scan_duration: Duration, device_name: &str) -> Result<Vec<Self>>
        +async fn single(device_name: Option<&str>) -> Result<Self>
        +async fn from_peripheral(peripheral: Peripheral, device_name: &str) -> Option<Self>
    }
Loading

Class diagram for main.rs argument and payload flow

classDiagram
    class Args {
        +Option<String> device_name
        +TransportProtocol transport
        +bool list_devices
    }
    class TransportProtocol
    class PayloadBuffer
    Args --> TransportProtocol
    Args --> PayloadBuffer
    class Main {
        +fn main() -> Result<()> 
        +fn write_payload(transport: &TransportProtocol, device_name: Option<String>, payload: PayloadBuffer) -> Result<(), anyhow::Error>
        +fn list_devices(transport: &TransportProtocol) -> Result<()> 
        +fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer>
    }
    Main --> Args
    Main --> TransportProtocol
    Main --> PayloadBuffer
Loading

File-Level Changes

Change Details Files
Parameterize BLE discovery and filtering with a dynamic device_name
  • Extend enumerate and enumerate_duration to accept a device_name argument
  • Update from_peripheral to compare against the passed device_name instead of the constant
  • Modify single to take an optional device_name and default to the original constant
src/ble.rs
Add --device_name CLI option and propagate it through payload writing
  • Introduce device_name field in Args and parse via clap
  • Update write_payload signature and invocation to include device_name
  • Pass device_name into BleDevice::single in main before writing the payload
src/main.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#56 Allow configuration of the BLE device name used for discovery and connection, instead of using a hardcoded value.
#56 Propagate the configurable BLE device name through the CLI and internal BLE scanning/connection logic.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @weberval - I've reviewed your changes - here's some feedback:

  • The new device_name parameter is currently taken as &String in multiple methods and is derived via &device_name.unwrap_or(...), which can lead to invalid references to temporaries; consider accepting &str or impl AsRef<str> (or an owned String) and restructuring to avoid borrowing from dropped values.
  • Propagating device_name through enumerate, enumerate_duration, and from_peripheral makes the API less ergonomic; consider encapsulating the BLE scan criteria (including device name) in a single config struct or using default arguments to reduce parameter plumbing.
  • In single, consuming an Option<String> only to immediately unwrap and reference it is both surprising and error-prone; it’d be clearer to accept either an owned String or Option<&str> and clone the fallback name when needed.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `device_name` parameter is currently taken as `&String` in multiple methods and is derived via `&device_name.unwrap_or(...)`, which can lead to invalid references to temporaries; consider accepting `&str` or `impl AsRef<str>` (or an owned `String`) and restructuring to avoid borrowing from dropped values.
- Propagating `device_name` through `enumerate`, `enumerate_duration`, and `from_peripheral` makes the API less ergonomic; consider encapsulating the BLE scan criteria (including device name) in a single config struct or using default arguments to reduce parameter plumbing.
- In `single`, consuming an `Option<String>` only to immediately unwrap and reference it is both surprising and error-prone; it’d be clearer to accept either an owned `String` or `Option<&str>` and clone the fallback name when needed.

## Individual Comments

### Comment 1
<location> `src/ble.rs:76` </location>
<code_context>
     /// Returns all badges that are in BLE range and are in Bluetooth transfer mode.
-    pub async fn enumerate() -> Result<Vec<Self>> {
-        Self::enumerate_duration(Duration::from_secs(2)).await
+    pub async fn enumerate(device_name: &String) -> Result<Vec<Self>> {
+        Self::enumerate_duration(Duration::from_secs(2), device_name).await
     }
</code_context>

<issue_to_address>
Consider using &str instead of &String for device_name parameter.

Using &str enables the function to accept both String and string literals, improving flexibility and adhering to Rust conventions.

Suggested implementation:

```rust
    pub async fn enumerate(device_name: &str) -> Result<Vec<Self>> {
        Self::enumerate_duration(Duration::from_secs(2), device_name).await
    }

```

```rust
    pub async fn enumerate_duration(
        scan_duration: Duration,
        device_name: &str,
    ) -> Result<Vec<Self>> {

```
</issue_to_address>

### Comment 2
<location> `src/ble.rs:85` </location>
<code_context>
     /// # Panics
     /// This function panics if it is unable to access the Bluetooth adapter.
-    pub async fn enumerate_duration(scan_duration: Duration) -> Result<Vec<Self>> {
+    pub async fn enumerate_duration(
+        scan_duration: Duration,
+        device_name: &String,
+    ) -> Result<Vec<Self>> {
         // Run device scan
</code_context>

<issue_to_address>
Use &str for device_name parameter for consistency and ergonomics.

This change improves usability, as device names are usually passed as string slices.

Suggested implementation:

```rust
    pub async fn enumerate(device_name: &str) -> Result<Vec<Self>> {
        Self::enumerate_duration(Duration::from_secs(2), device_name).await
    }

```

```rust
    pub async fn enumerate_duration(
        scan_duration: Duration,
        device_name: &str,
    ) -> Result<Vec<Self>> {

```

```rust
        {
            if let Some(badge) = Self::from_peripheral(p, device_name).await {
                led_badges.push(badge);
            }
        }

```

If there are any other places in the codebase where `enumerate` or `enumerate_duration` are called, ensure that the argument passed for `device_name` is a `&str` (e.g., use `"my_device"` or `my_string_variable.as_str()` if you have a `String`).
</issue_to_address>

### Comment 3
<location> `src/ble.rs:120` </location>
<code_context>
     }

-    async fn from_peripheral(peripheral: Peripheral) -> Option<Self> {
+    async fn from_peripheral(peripheral: Peripheral, device_name: &String) -> Option<Self> {
         // The existance of the service with the correct UUID
         // exists is already checked by the scan filter.
</code_context>

<issue_to_address>
Switch device_name parameter to &str for better ergonomics.

This update will enhance API usability across all call sites.

Suggested implementation:

```rust
            if let Some(badge) = Self::from_peripheral(p, device_name).await {

```

```rust
    async fn from_peripheral(peripheral: Peripheral, device_name: &str) -> Option<Self> {

```

```rust
        if local_name == device_name {

```
</issue_to_address>

### Comment 4
<location> `src/ble.rs:140` </location>
<code_context>
     /// or if multiple devices would match.
-    pub async fn single() -> Result<Self> {
-        let mut devices = Self::enumerate()
+    pub async fn single(device_name: Option<String>) -> Result<Self> {
+        let device_name = &device_name.unwrap_or(BADGE_BLE_DEVICE_NAME.to_string());
+        let mut devices = Self::enumerate(device_name)
</code_context>

<issue_to_address>
Consider accepting Option<&str> instead of Option<String> for device_name.

This change reduces allocations and increases flexibility, as the function only needs a borrowed value.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    pub async fn single(device_name: Option<String>) -> Result<Self> {
        let device_name = &device_name.unwrap_or(BADGE_BLE_DEVICE_NAME.to_string());
        let mut devices = Self::enumerate(device_name)
=======
    pub async fn single(device_name: Option<&str>) -> Result<Self> {
        let device_name = device_name.unwrap_or(BADGE_BLE_DEVICE_NAME);
        let mut devices = Self::enumerate(device_name)
>>>>>>> REPLACE

</suggested_fix>

### Comment 5
<location> `src/ble.rs:141` </location>
<code_context>
-    pub async fn single() -> Result<Self> {
-        let mut devices = Self::enumerate()
+    pub async fn single(device_name: Option<String>) -> Result<Self> {
+        let device_name = &device_name.unwrap_or(BADGE_BLE_DEVICE_NAME.to_string());
+        let mut devices = Self::enumerate(device_name)
             .await
</code_context>

<issue_to_address>
Avoid allocating a new String for BADGE_BLE_DEVICE_NAME if not necessary.

If BADGE_BLE_DEVICE_NAME is a &'static str, use as_ref() or pass &str directly to avoid unnecessary String allocation.

Suggested implementation:

```rust
    pub async fn single(device_name: Option<&str>) -> Result<Self> {

```

```rust
        let device_name = device_name.as_deref().unwrap_or(BADGE_BLE_DEVICE_NAME);
        let mut devices = Self::enumerate(device_name)

```

If `Self::from_peripheral` and other downstream functions expect a `String` or `&String`, you should update their signatures and usages to accept `&str` instead, to maintain consistency and avoid unnecessary allocations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@weberval weberval marked this pull request as draft July 20, 2025 08:29
@weberval

This comment was marked as outdated.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @weberval - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@weberval weberval marked this pull request as ready for review July 20, 2025 08:56
@weberval

This comment was marked as outdated.

@weberval weberval changed the title feat: add BLE device name config feat: add BLE device name config @sourcery-ai Jul 20, 2025
@sourcery-ai sourcery-ai bot changed the title feat: add BLE device name config @sourcery-ai Support custom BLE device name discovery Jul 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

B1144C not found via BLE after flashing FOSSASIA firmware: Name changed from "LSLED" to "LED Badge Magic"
1 participant