wakeword: reversible tray pause toggle; document quiet-session fixes
Bug: a stale /tmp/wake_muted flag silently disabled wakeword detection with no in-app way to clear it (the flag was read but never written by any code or UI). - wakeword.py: expose MUTE_FILE + is_muted()/set_muted() helpers - tray.py: add a "Pause wakeword" CheckMenuItem (shown when wakeword is enabled) that toggles the flag and reflects current state - config.py: document the tray toggle instead of the bare /tmp file - CHANGELOG: record the wakeword pause toggle and the existing notify hygiene fixes (transient/single-bubble notifications, silent hands-free sessions) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
73ab26c90b
commit
93de455b01
@ -9,6 +9,22 @@ The version is defined in [`blitztext/__init__.py`](blitztext/__init__.py).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Pause wakeword (tray)**: a reversible "Pause wakeword" toggle appears in the
|
||||||
|
system-tray menu when the wakeword is enabled. It pauses/resumes hands-free
|
||||||
|
detection by toggling the `/tmp/wake_muted` flag (external scripts may toggle
|
||||||
|
the same file).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Wakeword stuck muted**: a leftover `/tmp/wake_muted` flag silently disabled
|
||||||
|
detection with no in-app way to clear it. The state is now exposed and
|
||||||
|
reversible from the tray, so a stale flag no longer kills hands-free use.
|
||||||
|
- **Notification storm / lock-screen pile-up**: desktop notifications are now
|
||||||
|
sent as transient with a short expiry and reuse a single bubble, so they no
|
||||||
|
longer stack in the notification log or persist on the lock screen.
|
||||||
|
- **Quiet hands-free sessions**: per-dictation notifications are suppressed for
|
||||||
|
wakeword-triggered sessions (audio cues are used instead).
|
||||||
|
|
||||||
## [1.2.0] - 2026-06-05
|
## [1.2.0] - 2026-06-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@ -87,6 +87,20 @@ class Tray:
|
|||||||
menu.append(item)
|
menu.append(item)
|
||||||
|
|
||||||
menu.append(Gtk.SeparatorMenuItem())
|
menu.append(Gtk.SeparatorMenuItem())
|
||||||
|
|
||||||
|
# Hands-free wakeword: a reversible pause toggle. Without this, a stale
|
||||||
|
# /tmp/wake_muted flag would silently disable detection with no way back.
|
||||||
|
if getattr(self.app.cfg, "wakeword_enabled", False):
|
||||||
|
from . import wakeword
|
||||||
|
|
||||||
|
self.mute_item = Gtk.CheckMenuItem(label="Pause wakeword")
|
||||||
|
self.mute_item.set_active(wakeword.is_muted())
|
||||||
|
self.mute_item.connect(
|
||||||
|
"toggled", lambda i: wakeword.set_muted(i.get_active())
|
||||||
|
)
|
||||||
|
menu.append(self.mute_item)
|
||||||
|
menu.append(Gtk.SeparatorMenuItem())
|
||||||
|
|
||||||
for label, cb in (
|
for label, cb in (
|
||||||
("Show panel", self.app.show_panel),
|
("Show panel", self.app.show_panel),
|
||||||
("Settings…", self.app.open_settings),
|
("Settings…", self.app.open_settings),
|
||||||
|
|||||||
@ -19,7 +19,24 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
from . import logbuffer
|
from . import logbuffer
|
||||||
|
|
||||||
_MUTE_FILE = "/tmp/wake_muted"
|
MUTE_FILE = "/tmp/wake_muted"
|
||||||
|
|
||||||
|
|
||||||
|
def is_muted() -> bool:
|
||||||
|
"""True if wakeword detections are currently paused via the mute flag."""
|
||||||
|
return os.path.exists(MUTE_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
def set_muted(muted: bool) -> None:
|
||||||
|
"""Pause (create flag) or resume (remove flag) wakeword detection."""
|
||||||
|
try:
|
||||||
|
if muted:
|
||||||
|
open(MUTE_FILE, "a").close()
|
||||||
|
elif os.path.exists(MUTE_FILE):
|
||||||
|
os.remove(MUTE_FILE)
|
||||||
|
except OSError as e: # noqa: BLE001 - mute is best-effort, never crash
|
||||||
|
logbuffer.log(f"[wakeword] Could not update mute flag: {e}")
|
||||||
|
|
||||||
|
|
||||||
class WakewordListener:
|
class WakewordListener:
|
||||||
def __init__(self, uri: str, model: str, mic: str, on_detect):
|
def __init__(self, uri: str, model: str, mic: str, on_detect):
|
||||||
@ -142,8 +159,8 @@ class WakewordListener:
|
|||||||
if time.time() < self._cooldown_until:
|
if time.time() < self._cooldown_until:
|
||||||
return
|
return
|
||||||
|
|
||||||
if os.path.exists(_MUTE_FILE):
|
if is_muted():
|
||||||
logbuffer.log("[wakeword] Detected, but muted via /tmp/wake_muted")
|
logbuffer.log("[wakeword] Detected, but paused (resume via tray)")
|
||||||
return
|
return
|
||||||
|
|
||||||
logbuffer.log(f"[wakeword] Detected '{self.model}'!")
|
logbuffer.log(f"[wakeword] Detected '{self.model}'!")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user