Files

65 lines
3.3 KiB
Markdown

# Architecture
The protocol module has no Bluetooth or GUI dependencies. Tk exists only in
`controller/gui.py`, which `cli.py` imports after parsing arguments. Importing
the package or running `--help` does not load Tk or Bleak.
| Module | Responsibility |
| --- | --- |
| `protocol.py` | Packet encoding, stream framing, observed checksum variants |
| `commands.py` | Semantic command builders; no packet layout in Tk callbacks |
| `session.py` | Handshake, outgoing sequence numbers, connection readiness |
| `messages.py` | English command/reply descriptions and bounded activity history |
| `worker.py` | Thread lifecycle and event/command queues |
| `ble_link.py` | Bleak connection, notifications, characteristic writes |
| `link.py`, `transport.py` | Experimental RFCOMM and serial adapters |
| `gui.py` | Tk controls and event rendering |
| `discovery.py` | Read-only BLE discovery, unnamed model matching, and selectable labels |
| `cli.py`, `__main__.py` | Installed command and backwards-compatible module entry |
| `ble_probe.py`, `probe.py` | Endpoint diagnostics |
| `windows_bluetooth.py` | Experimental RFCOMM initialization |
## Session interface
Create one `Session` per connection. Write `session.start()` to begin. Pass
received chunks to `session.receive(chunk)` and write every returned `outgoing`
packet. `Update.frames` carries decoded packets; `became_ready` is emitted once
when the speaker acknowledges the link-complete request. Use
`session.command(payload)` for subsequent outgoing packets.
Both transports cross this same seam. The session owns framing and the state
machine, so fixes do not need to be repeated in separate Bluetooth loops.
A transport connection, a GATT write response, a Samsung packet acknowledgment,
and a speaker state report are distinct events. Only the link-complete ACK
makes the session ready. Speaker notifications have an independent sequence
counter and cannot substitute for that ACK.
## Threads and ownership
Tk runs on the main thread. Each connection worker owns its transport. BLE's
async event loop lives entirely within the worker thread. The GUI submits
payloads through a bounded command queue and consumes events through another
queue; workers never call Tk methods.
Discovery uses a separate bounded-duration worker and the same event queue.
It returns plain speaker records without opening a GATT connection. Tk maps a
selected label to its address; manual addresses remain supported. Scanning is
disabled while a connection is active so scans do not compete with BLE traffic.
BLE notifications enter a bounded async queue and can contain partial or
combined packets. The decoder retains framing state between notifications.
Queue overflow terminates the connection rather than silently losing framing.
Writes are spaced and never automatically retried. Retrying a volume or mute
key could perform the action twice. The GUI enables controls only after the
protocol handshake and disables them on connection closure.
## Testing
Protocol tests use captured byte fixtures independently of the outgoing
encoder. Session tests cover split replies, duplicate replies, and ACK matching.
Transport tests verify subscription order, write mode, disconnects, resource
cleanup, and readable error phases. Presentation tests verify the difference
between acknowledgment and a reported state. No test opens physical Bluetooth.