3 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.8 ccb46d45b4 release v0.2.1
Test, Build and Push / test-gateway (push) Successful in 11s
Test, Build and Push / release (push) Successful in 2s
Test, Build and Push / build-gateway (push) Successful in 1m27s
Fixes: volume voice command no longer hangs in the thinking spinner (gong
stops holding the busy state), and the butler filler is reworded to avoid
a text-to-speech mid-phrase pause.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu
2026-07-15 22:39:46 +02:00
jpmschweitzerandClaude Opus 4.8 9a62233775 fix(gateway): reword butler filler to avoid a TTS mid-phrase pause
Kokoro inserts an unnatural ~0.2s pause before "for you", so "Let me check
on that for you, sir." came out as two phrases. Reword to "Let me check on
that, sir." — same intent, clean pacing (measured: no internal silence gap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu
2026-07-15 22:39:03 +02:00
jpmschweitzerandClaude Opus 4.8 24e9375095 fix(firmware): don't let the feedback gong swallow return-to-idle
A spoken volume command left the face stuck in the thinking spinner. The
gateway sends state:thinking -> command -> state:idle, but the device's
state:idle handler is gated on !audio_is_playing(), and the feedback gong
had been setting s_playing for its (up to 5 s) duration — so the idle was
ignored and nothing re-sent it. The gong is a UI cue, not reply playback,
so it no longer sets s_playing. This also drops the 5 s wake-gate the gong
was imposing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu
2026-07-15 21:50:53 +02:00
4 changed files with 17 additions and 5 deletions
+10
View File
@@ -8,6 +8,16 @@ until the first tagged release.
## [Unreleased] ## [Unreleased]
## [0.2.1] — 2026-07-15
### Fixed
- A spoken volume command no longer leaves the face stuck in the thinking
spinner — the feedback gong is a UI cue and no longer holds the busy state,
so the return-to-idle isn't swallowed.
- The butler filler line reads as one phrase instead of two — reworded to "Let
me check on that, sir." to avoid a text-to-speech pause before "for you".
## [0.2.0] — 2026-07-15 ## [0.2.0] — 2026-07-15
### Added ### Added
+4 -3
View File
@@ -161,8 +161,10 @@ static void audio_task(void *arg)
* returns, preventing a playback-boundary false wake. */ * returns, preventing a playback-boundary false wake. */
if (job == JOB_GONG && s_gong != NULL) { if (job == JOB_GONG && s_gong != NULL) {
/* Play in chunks so a new request (a fresh volume change) cuts the /* Play in chunks so a new request (a fresh volume change) cuts the
* current gong off and restarts, instead of queueing another 5 s. */ * current gong off and restarts, instead of queueing another 5 s.
s_playing = true; * Note: the gong is a UI cue, not "playback" — it deliberately does
* NOT set s_playing, so a following state:idle isn't swallowed (that
* guard is for reply audio) and it doesn't gate wake for 5 s. */
uint32_t gen; uint32_t gen;
do { do {
gen = s_gong_gen; gen = s_gong_gen;
@@ -172,7 +174,6 @@ static void audio_task(void *arg)
esp_codec_dev_write(s_spk, s_gong + off, n * sizeof(int16_t)); esp_codec_dev_write(s_spk, s_gong + off, n * sizeof(int16_t));
} }
} while (s_gong_gen != gen); /* a new request arrived mid-play → restart */ } while (s_gong_gen != gen); /* a new request arrived mid-play → restart */
s_playing = false;
s_gong_active = false; s_gong_active = false;
} else if (job == JOB_CHIME && s_chime != NULL) { } else if (job == JOB_CHIME && s_chime != NULL) {
s_playing = true; s_playing = true;
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "desklock-gateway" name = "desklock-gateway"
version = "0.2.0" version = "0.2.1"
description = "Voice gateway bridging the DeskLock device to the Tatlock butler (STT/chat/TTS)" description = "Voice gateway bridging the DeskLock device to the Tatlock butler (STT/chat/TTS)"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
+2 -1
View File
@@ -20,7 +20,8 @@ class Settings(BaseSettings):
tts_voice: str = "bm_george" tts_voice: str = "bm_george"
# Spoken immediately after a query is accepted, to fill the long Tatlock wait. # Spoken immediately after a query is accepted, to fill the long Tatlock wait.
filler_text: str = "Let me check on that for you, sir." # Avoid "for you" — Kokoro inserts an unnatural mid-phrase pause before it.
filler_text: str = "Let me check on that, sir."
# embedded fallback only (requires the [speech] extra) # embedded fallback only (requires the [speech] extra)
embedded_stt_model: str = "small" embedded_stt_model: str = "small"