README: document Plan 3 Pi client surface
This commit is contained in:
@@ -137,10 +137,77 @@ PY
|
||||
Don't send `wake` from the smoke — it would open a real OpenAI session and
|
||||
burn budget. Plan 3 exercises that end-to-end.
|
||||
|
||||
## Plan 3: Pi client
|
||||
|
||||
The Python client lives in `client/`. It is shipped to the Pi inside
|
||||
`client.tar.gz` (built into the backend Docker image) and installed by
|
||||
`install.sh`. Modules:
|
||||
|
||||
- `main.py` — entry point. Sets `PA_ALSA_PLUGHW=1` before importing
|
||||
sounddevice. Wires audio, state, wakeword, session, and playback.
|
||||
- `state.py` — `IDLE → WAKE_PENDING → LISTENING ↔ ASSISTANT_SPEAKING →
|
||||
IDLE_PENDING → IDLE`. `wakeword_enabled` is true only in `IDLE`;
|
||||
`uplink_enabled` is true only in `LISTENING`.
|
||||
- `audio.py` — USB device discovery, persistent 24 kHz/mono/int16 InputStream
|
||||
and OutputStream, and the 24 → 16 kHz resample for the wakeword feed.
|
||||
- `wakeword.py` — openwakeword `alexa` model, threshold + cooldown.
|
||||
- `playback.py` — single worker thread that owns the OutputStream and
|
||||
implements `set_volume` as a software gain (the USB Speaker Phone has no
|
||||
hardware playback volume control; only a `PCM Playback Switch`).
|
||||
- `session.py` — backend WS client, dispatch, tool round-trip,
|
||||
exponential-backoff reconnect.
|
||||
- `config.py` — `~/assistant/state/config.json` (mode 0600).
|
||||
- `pair.py` — `python -m client.pair --backend <url> [--code <code>]`.
|
||||
|
||||
### Updating the Pi
|
||||
|
||||
For a fast iteration loop:
|
||||
|
||||
```sh
|
||||
sshpass -p 'assistant' rsync -az --delete --exclude __pycache__ --exclude tests \
|
||||
client/ pi@192.168.50.115:assistant/code/client/
|
||||
sshpass -p 'assistant' ssh pi@192.168.50.115 'systemctl --user restart assistant'
|
||||
sshpass -p 'assistant' ssh pi@192.168.50.115 \
|
||||
'journalctl --user -u assistant -n 50 --no-pager'
|
||||
```
|
||||
|
||||
For a production update (after a backend deploy):
|
||||
|
||||
```sh
|
||||
sshpass -p 'assistant' ssh pi@192.168.50.115 \
|
||||
'curl -fsSL https://assistant.volcanic.tes.gd/install.sh | bash'
|
||||
```
|
||||
|
||||
### Local tests
|
||||
|
||||
```sh
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements-dev.txt requests websockets numpy scipy
|
||||
.venv/bin/python -m pytest client/tests
|
||||
```
|
||||
|
||||
Hardware-touching modules (`audio.py` streams, `wakeword.py`, `playback.py`,
|
||||
`session.run_forever`) are smoke-tested via the live deploy. Deterministic
|
||||
modules (`state.py`, `config.py`, `pair.py`, the resample helper, the session
|
||||
dispatcher) are covered by the pytest suite.
|
||||
|
||||
### End-to-end smoke
|
||||
|
||||
With the unit running on the Pi:
|
||||
|
||||
```sh
|
||||
sshpass -p 'assistant' ssh pi@192.168.50.115 'journalctl --user -u assistant -f'
|
||||
```
|
||||
|
||||
…then say `alexa` near the Speaker Phone. Expected log sequence: `wakeword
|
||||
fired`, `state IDLE -> WAKE_PENDING`, `session_started conversation_id=…`,
|
||||
`state WAKE_PENDING -> LISTENING`. Speak a question; the assistant replies
|
||||
through the speaker (`state LISTENING -> ASSISTANT_SPEAKING`, then
|
||||
`assistant_done`). Say "bye" → `session_ended reason=tool`, then `state
|
||||
LISTENING -> IDLE_PENDING -> IDLE`. The conversation row + turns are visible
|
||||
via the deployed backend's DB (admin UI in Plan 4).
|
||||
|
||||
## What's next
|
||||
|
||||
- **Plan 3** — full Python client on the Pi (wakeword, VAD, audio plumbing,
|
||||
state machine, install + pair CLIs). This is the production caller of
|
||||
`/device` and the integration test for everything above.
|
||||
- **Plan 4** — React management UI for the admin dashboard, per-device
|
||||
config editor, and conversation history viewer.
|
||||
|
||||
Reference in New Issue
Block a user