Skip to content

About

Bose® Connect App for Linux [Not Official]

Topics

Resources

Contributing

Stars

48 stars

Watchers

3 watching

Forks

Repository files navigation

Bose® Connect App for Linux

Not an official app.

A reverse-engineered Linux port of the Bose Connect app, originally forked from Denton-L/based-connect. This repository keeps the original GPL-3.0 license.

If you own a Bose device, you'll know that Bose Connect is not available on Linux. This program re-implements the RFCOMM protocol the official app speaks so the device can be controlled from Linux.

This is the Rust port of the codebase. The original C implementation lives on the main branch's git history; see git log --follow src/library/based.c for the last C version.

Repository layout

.
├── Cargo.toml                # workspace manifest
├── crates/
│   ├── bose-connect/         # library crate (Rust API + C FFI)
│   │   ├── src/
│   │   │   ├── lib.rs        # public Rust API
│   │   │   ├── connection.rs # RFCOMM socket layer
│   │   │   ├── protocol.rs   # Bose Connect protocol commands
│   │   │   ├── io.rs         # transport-agnostic IO trait
│   │   │   ├── types.rs      # enums + BdAddr + Device
│   │   │   ├── error.rs      # BoseError + thiserror
│   │   │   ├── util.rs       # hex / byte helpers
│   │   │   ├── address.rs    # Bluetooth address parsing
│   │   │   ├── ffi.rs        # C ABI surface (cfg(feature = "ffi"))
│   │   │   └── bin/probe.rs  # sanity-probe binary
│   │   ├── tests/
│   │   │   └── protocol_roundtrip.rs  # UnixStream-backed round-trip tests
│   │   ├── cbindgen.toml     # C header generation config
│   │   └── build.rs          # cbindgen invocation
│   └── bose-connect-cli/     # binary crate
│       └── src/main.rs       # clap-based CLI
└── .github/workflows/        # CI / CD / release

Quick start

Library (bose-connect)

Add to your Cargo.toml:

[dependencies]
bose-connect = "0.1"

Use the high-level BoseDevice driver:

use bose_connect::{BoseDevice, PromptLanguage};

let mut device = BoseDevice::open("AA:BB:CC:DD:EE:FF")?;
println!("Battery: {}%", device.battery_level()?);
device.set_language_keep_voice_prompts(PromptLanguage::En)?;

Or drop down to the protocol layer directly:

use bose_connect::protocol;

let mut device = BoseDevice::open("AA:BB:CC:DD:EE:FF")?;
let (device_id, index) = protocol::get_device_id(device.connection())?;
let firmware = protocol::get_firmware_version(device.connection())?;

CLI (bose-connect-app-linux)

Build from source:

cargo install --path crates/bose-connect-cli

Then run, exactly like the original C version (every flag is preserved):

Usage: bose-connect-app-linux [options] <address>
  # address: The Bluetooth address of the Bose's device.

  -h, --help
    Print the help message.
  -i, --info
    Print all the device information.
  -d, --device-status
    Print the device status information.
  -f, --firmware-version
    Print the firmware version on the device.
  -s, --serial-number
    Print the serial number of the device.
  -b, --battery-level
    Print the battery level of the device as a percent.
  -a, --paired-devices
    Print the devices currently connected to the device.
  --device-id
    Print the device id followed by the index revision.
  -n <name>, --name=<name>
    Change the name of the device.
  -o <minutes>, --auto-off=<minutes>
    Change the auto-off time.  minutes: never, 5, 20, 40, 60, 180
  -c <level>, --noise-cancelling=<level>
    Change the noise cancelling level.  level: high, low, off
  -l <language>, --prompt-language=<language>
    Change the voice-prompt language.
    language: en, fr, it, de, es, pt, zh, ko, nl, ja, sv
  -v <switch>, --voice-prompts=<switch>
    Change whether voice-prompts are on or off.  switch: on, off
  -p <status>, --pairing=<status>
    Change whether the device is pairing.  status: on, off
  -e, --self-voice=<level>
    Change the self voice level.  level: high, medium, low, off
  --connect-device=<address>
    Attempt to connect to the device at address.
  --disconnect-device=<address>
    Disconnect the device at address.
  --remove-device=<address>
    Remove the device at address from the pairing list.
  -m <mode>, --audio-mode=<mode>
    Change the audio mode (QC Ultra).
    mode: quiet, aware, immersion, or a slot index
  --channel=<channel>
    Use this RFCOMM channel instead of the automatic choice (8, then 2).

C / FFI

The library ships a stable C ABI through the ffi feature (default-on). The header is auto-generated by cbindgen into crates/bose-connect/bose_connect.h at build time:

cargo build -p bose-connect
ls crates/bose-connect/bose_connect.h

Build a shared library for non-Rust consumers:

cargo build --release -p bose-connect \
  --crate-type cdylib

# The resulting .so is at target/release/libbose_connect.so

Build and Installation

Dependencies

  • Rust toolchain (1.85 or newer; tested on stable)
  • pkg-config
  • BlueZ headers
    • bluez-libs on Arch Linux
    • libbluetooth-dev on Debian and Ubuntu

The Rust crate builds on Linux without the BlueZ headers — it calls the kernel through libc::connect directly. The headers are required only for downstream C/C++ projects that include the generated header in environments without the BlueZ userspace headers installed.

Local

# Run the CI gauntlet locally (matches .github/workflows/ci.yml).
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
cargo doc --no-deps --workspace
cargo build --workspace --release --locked

# Smoke-test the binary.
./target/release/bose-connect-app-linux --help

Docker

The original C repo's Docker setup is replaced by a rust:1.83-bookworm-based image (Dockerfile / compose TBD; tracked in TODO.md). For now, install the toolchain locally.

CI/CD

The .github/workflows/ directory contains:

File Purpose
ci.yml Rust CI: fmt, clippy, test, doc, release build
codeql.yml GitHub CodeQL security analysis
cargo-audit.yml RustSec advisory scanner, nightly
release.yml Tag-triggered release: crates.io publish + GitHub release with binary artefacts

Migrating from the C version

C source file Rust counterpart
src/main.c crates/bose-connect-cli/src/main.rs
src/library/based.c crates/bose-connect/src/protocol.rs
src/library/bluetooth.c crates/bose-connect/src/connection.rs (+ address.rs)
src/library/util.c crates/bose-connect/src/util.rs
src/library/based.h (enums) crates/bose-connect/src/types.rs

The on-wire protocol is unchanged: every byte sequence, masked-ACK mask, and short-read / short-write semantic is preserved verbatim from the original C. If a regression slips through, git diff feat/migration-to-rust main -- src/library/based.c shows the source-of-truth protocol implementation side-by-side.

QuietComfort Ultra Headphones

The QC Ultra Headphones (device id 0x4066) speak the same protocol on RFCOMM channel 2 instead of 8; the connection falls back to it automatically (--channel forces one). Their audio modes replace the noise-cancelling levels:

# mode: quiet, aware, immersion, or a slot index
bose-connect-app-linux AA:BB:CC:DD:EE:FF --audio-mode aware

Supported: --info, --device-status (including the current audio mode), --firmware-version, --serial-number, --battery-level, --paired-devices, --device-id, --audio-mode and --self-voice. --auto-off, --prompt-language and --voice-prompts are refused because the QC Ultra's payloads for them differ from the QC35's and have not been decoded yet.

Disclaimer

This has only been tested on Bose QuietComfort 35's with firmware 1.3.2, 1.2.9, 1.06, SoundLink II's with firmware 2.1.1 and QuietComfort Ultra Headphones with firmware 1.6.7. I cannot ensure that this program works on any other devices.

About

Bose® Connect App for Linux [Not Official]

Topics

Resources

Contributing

Stars

48 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages