65 lines
3.3 KiB
Markdown
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.
|