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.
.
├── 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
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())?;Build from source:
cargo install --path crates/bose-connect-cliThen 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).
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.hBuild 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- Rust toolchain (1.85 or newer; tested on stable)
pkg-config- BlueZ headers
bluez-libson Arch Linuxlibbluetooth-devon 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.
# 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 --helpThe 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.
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 |
| 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.
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 awareSupported: --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.
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.