Skip to content

feat(lib): dual connections for the D1301 (Sleep A30) - #394

Open
joeyjoe02 wants to merge 2 commits into
Oppzippy:masterfrom
joeyjoe02:d1301-dual-connections
Open

joeyjoe02 wants to merge 2 commits into
Oppzippy:masterfrom
joeyjoe02:d1301-dual-connections

Conversation

@joeyjoe02

@joeyjoe02 joeyjoe02 commented Oct 12, 2026 •

Copy link
Copy Markdown

This change was found, tested and written by Claude on my machine, at my request. I'm not a developer; I'm submitting it because it may help other Sleep A30 owners. Happy for it to be reworked or closed if you'd rather do it differently.

What

Dual Connections for the Soundcore Sleep A30 (D1301): the common dual connections module (toggle, device list, connect/disconnect/forget), plus two D1301-specific pieces, because the A30 neither reports whether dual connections is enabled nor remembers it after powering off:

  • On connect the device list (0b 01) is always requested. The A30 answers it even while dual connections is off, and the state update packet has no enabled flag to check first.
  • Enabled is assumed when two devices are connected; otherwise it shows the last value set through OpenSCQ30.
  • The last value set through OpenSCQ30 is saved and, if it was on, 0b 84 01 is sent again on connect. It's compared against the saved value, not the state, because with two devices connected the state already shows enabled; a side effect is that changing any setting while two devices are connected saves "on".

The value is saved per device model in a new device_model_setting table (migration 1.sql), meant for settings a device accepts but can't report back or doesn't keep. Two small additions to shared code: SoundcoreDeviceBuilder::state_sender(), so a module can update state while the device is being built, and TestSoundcoreDevice::new_with_database, so tests can reconnect with the same database.

Why

The A30's firmware (01.07) accepts the same 0x0b commands as the multipoint models, and dual connections works once enabled (two hosts connected, audio from both), but neither the Soundcore app nor OpenSCQ30 exposes it for this model. Testing on a Sleep A30 Special:

command A30 reply effect
0b 01 device list standard DualConnectionsDevicePacket, 2 packets the 5 remembered hosts with names and connected flags
0b 84 01 / 00 01 00 / 00 00 on: a second host can connect alongside the first. off: the A30 drops the other host and pushes 0b 01
0b 81 <mac> disconnect 00 00 00 00 01 01 (… ff 1f for an unknown MAC) host dropped within ~80 ms
0b 82 <mac> connect echoes the MAC, then pushes 0b 02 00 <mac> host reconnected after ~4 s
0b 83 <mac> forget echoes the MAC only tried with a MAC that isn't in the list

Unknown commands get no reply at all, so these are implemented, not just ignored.

No read-back. With one host connected I toggled 0b 84 six times and, after each toggle, read every command that replies (cmd0 0x00–0x1f × cmd1 0x00–0x0f, plus 0b 10–0b 7f; 26 reply). The 01 01 state packet was byte-identical across 11 consecutive toggles, and nothing else tracked the flag either. For comparison, the D1202 reports dual_connections_enabled right after LowBatteryPrompt; the D1301 has its unknown 67-byte block in that position, but nothing in it changes.

Not persisted. The flag survives closing and opening the case, but was off again twice after the buds had been unused for several hours. With it off and one host connected, the A30 doesn't answer pages at all (br-connection-page-timeout), so it has to be turned back on from the host that's connected.

Testing

  • 8 new tests: storage fetch/upsert, off when unknown, on when two devices are connected, enabling sends 0b 84 01, re-applied on reconnect, re-applied when enabled while already on, and not re-applied after being turned off. I checked that the reconnect tests fail without the re-apply step and without the second commit's fix.
  • cargo test -p openscq30-lib -p openscq30-cli: all pass. cargo fmt --check is clean, and clippy shows the same 129 warnings before and after (none in changed code).
  • On real buds (Sleep A30 Special, firmware 01.07, Zorin OS / BlueZ 5.72), using the CLI with a temporary config directory: the Dual Connections category lists the 5 remembered hosts. I set dualConnections=true, turned it off behind OpenSCQ30's back, and reconnected. OpenSCQ30 sent 0b 84 01 again and the buds acknowledged it (01 00). Installing the build over 2.12.0 with my existing config migrated the database to version 2. That's where I found the second commit's bug: with my phone and laptop both connected, enabling wasn't saved. After the fix it is.

Not tested

  • The GUI and Android app. Only the library and CLI were run against the buds.
  • Forget (0b 83) on a real remembered host.
  • A third host while two are connected.
  • Firmware other than 01.07 (the FCC test unit ran V01.04).

Open questions

  • Is a new table OK, or would you rather keep the D1301 stateless (e.g. a one-shot "enable" action instead of a remembered toggle)?
  • The dual connections device list shows MAC addresses in wire byte order (my laptop 4C:A9:54:F6:B8:40 appears as 40:B8:F6:54:A9:4C). That's the existing shared parser and not changed here; I'm only mentioning it in case it's unintended.

🤖 Generated with Claude Code

joeyjoe02 and others added 2 commits October 12, 2026 11:17
The D1301's firmware accepts the same dual connections commands as the other
multipoint models (0b01 device list, 0b84 enable, 0b81/0b82/0b83
disconnect/connect/forget) and dual connections works once enabled, but it
neither reports the enabled flag (the state update packet is byte-identical
with it on or off) nor keeps it after the earbuds fully power off.

The common dual connections module is added to the D1301, and the D1301 adds:
- the device list is always requested on connect, since the D1301 answers it
  even while dual connections is disabled
- enabled is assumed when two devices are connected, otherwise off
- the last value set through openscq30 is saved and, if it was on, sent again
  on connect

The value is saved per device model in a new device_model_setting table
(migration 1), for settings a device accepts but cannot report back or does
not keep. SoundcoreDeviceBuilder::state_sender is added so a module can
update state while the device is being built, and
TestSoundcoreDevice::new_with_database so tests can reconnect with the same
database.

Tested on a Sleep A30 Special (firmware 01.07): enabling dual connections
lets a second host connect alongside the first, and after disabling it
behind openscq30's back, reconnecting sends 0b84 01 again and the earbuds
acknowledge it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
With two devices connected the D1301's dual connections is already shown as
enabled, so setting it to on was not a change from the state and was never
saved, and it was not reapplied after the earbuds were powered off. Compare
against the saved value instead of the state.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
sanjaymalladi added a commit to sanjaymalladi/OpenSCQ30 that referenced this pull request Oct 13, 2026
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.

1 participant