Skip to content

Bluetooth menu - #235

Open
sastraxi wants to merge 13 commits into
mainfrom
feat/bluetooth-menu
Open

Bluetooth menu#235
sastraxi wants to merge 13 commits into
mainfrom
feat/bluetooth-menu

Conversation

@sastraxi

@sastraxi sastraxi commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Adds a bluetooth sub-menu inside of the Wi-Fi menu (now renamed Wi-Fi & Devices).

image

Requires the pistomp-bluetooth package to be present, otherwise this menu is hidden. Alternatively, manually unblock via rfkill (needs to be installed) and add the -E bluetooth daemon option.

Author checklist

  • Initial on-device testing
  • Exhaustive on-device testing
  • UX tweaks
  • Remove plan markdown

sastraxi and others added 4 commits August 7, 2026 15:03
Pair, connect, and forget BLE-MIDI devices from the LCD. Reached from a
"Bluetooth..." footer button on the Wi-Fi menu, shown only when the board
has an adapter — Pi 3/4 hand the BT UART to DIN MIDI, so those users see
no mention of it.

modalapi/bluetooth/ mirrors modalapi/wifi/: a dbus-fast client owning an
asyncio thread, an org.bluez.Agent1 (NoInputNoOutput — without a
registered agent headless pairing cannot complete at all), stateless
verbs, and a BluetoothManager driving the shared CommandQueue.

Three findings from the hardware investigation shape the design:

- BlueZ purges unpaired LE device objects the instant discovery stops, so
  discovery is held open for as long as the nearby list is on screen and
  Pair() is issued against a live object while it runs. Discovery is a
  start/stop pair, not a blocking scan like wifi's.
- Trusting a device makes bluez auto-connect on sight, which then makes
  our own Pair() return InProgress. So: pair first, trust second, and
  treat InProgress as "wait for the running attempt", not a failure.
- Some devices (the EV-1-WL) refuse bonding, so a plain Disconnect drops
  them back to unpaired. The root list is therefore the union of bluez's
  paired set and our own known-device store, and a known-but-absent row
  says "press its button" rather than "Disconnected".

The menu also detects a bluetoothd running without -E and offers to
install pistomp-bluetooth, rather than pairing into a void that produces
no ALSA seq port.

CommandQueue moves to common/ so bluetooth doesn't depend on wifi. Menu
gains footer buttons and a real width parameter; max_width was dead code
(its clamp reassigned the same hardcoded 240). Existing menus keep that
width, the Bluetooth menu is wider.

HID input (pistomp/hid_controller.py) is deliberately not in this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread docs/bluetooth-menu.md Outdated
Comment thread docs/bluetooth-menu.md Outdated

@rreichenbach rreichenbach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Didn't actually try it.

sastraxi and others added 9 commits August 29, 2026 13:25
Turning Bluetooth off stopped bluetoothd but left the radio up: set_enabled
dropped the D-Bus client first, so nothing ever set Powered=false, and the
rfkill soft block that pistomp-bluetooth's drop-in clears on start was never
re-applied. Whether hci0 actually went down was left to bluetoothd's exit
behaviour.

Off now powers the adapter down over D-Bus while bluetoothd is still alive to
answer -- the ordering is load-bearing, since the adapter's object disappears
with the daemon -- then stops the client, disables the unit, and re-applies the
rfkill soft block as the guarantee. On writes sysfs to unblock before starting
the unit. rfkill(8) is not installed on the image, so this writes
/sys/class/rfkill/*/soft directly, mirroring the drop-in's ExecStartPre.

Also correct the claim that Bluetooth hardware is Pi 5 only. config.txt pairs
dtoverlay=pi3-disable-bt with dtoverlay=pi3-miniuart-bt, and the latter wins:
BT moves to the miniUART rather than being removed, and DIN MIDI keeps the
PL011 on GPIO14/15. A Pi 3A+ on the 3.3 image registers hci0 and boots
rfkill soft-blocked exactly like a Pi 5, so the menu already appears there.
Renames test_has_adapter_false_on_pi3_pi4, which encoded that false premise
while only ever testing an empty sysfs directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxE1HgwDHoLEAYkiExnTxw
# Conflicts:
#	pyproject.toml
#	uilib/menu.py
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.

2 participants