voice mode fixes

This commit is contained in:
techartdev
2026-02-21 00:58:59 +02:00
parent 4aba4bd18d
commit 14c5d946a5
5 changed files with 63 additions and 24 deletions
+7
View File
@@ -2,6 +2,13 @@
All notable changes to the OpenClaw Home Assistant Integration will be documented in this file. All notable changes to the OpenClaw Home Assistant Integration will be documented in this file.
## [0.1.45] - 2026-02-21
### Fixed
- Hardened voice startup/toggle flow in the chat card to prevent voice mode from getting stuck when browser speech startup fails.
- Added guarded async click handlers for voice buttons to avoid uncaught promise failures during voice mode enable/disable.
- Browser speech startup failures now set a clear in-card voice status and automatically revert continuous voice mode.
## [0.1.44] - 2026-02-21 ## [0.1.44] - 2026-02-21
### Fixed ### Fixed
+1 -1
View File
@@ -92,7 +92,7 @@ _CARD_PATH = Path(__file__).parent / "www" / _CARD_FILENAME
# URL at which the card JS is served (registered via register_static_path) # URL at which the card JS is served (registered via register_static_path)
_CARD_STATIC_URL = f"/openclaw/{_CARD_FILENAME}" _CARD_STATIC_URL = f"/openclaw/{_CARD_FILENAME}"
# Versioned URL used for Lovelace resource registration to avoid stale browser cache # Versioned URL used for Lovelace resource registration to avoid stale browser cache
_CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.44" _CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.45"
OpenClawConfigEntry = ConfigEntry OpenClawConfigEntry = ConfigEntry
+1 -1
View File
@@ -8,7 +8,7 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues", "issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
"requirements": [], "requirements": [],
"version": "0.1.44", "version": "0.1.45",
"dependencies": ["conversation"], "dependencies": ["conversation"],
"after_dependencies": ["hassio", "lovelace"] "after_dependencies": ["hassio", "lovelace"]
} }
@@ -615,19 +615,25 @@ class OpenClawChatCard extends HTMLElement {
return this._voiceProviderIntegration || "browser"; return this._voiceProviderIntegration || "browser";
} }
_startVoiceRecognition() { async _startVoiceRecognition() {
const provider = this._getVoiceProvider(); const provider = this._getVoiceProvider();
try {
if (this._isVoiceMode && provider === "assist_stt") { if (this._isVoiceMode && provider === "assist_stt") {
this._voiceStatus = this._voiceStatus =
"Voice mode uses browser speech for continuous listening (Assist STT remains available for manual mic)."; "Voice mode uses browser speech for continuous listening (Assist STT remains available for manual mic).";
this._startBrowserVoiceRecognition(); return this._startBrowserVoiceRecognition();
return;
} }
if (provider === "assist_stt") { if (provider === "assist_stt") {
this._startAssistSttRecognition(); return await this._startAssistSttRecognition();
return; }
return this._startBrowserVoiceRecognition();
} catch (err) {
console.error("OpenClaw: voice startup failed", err);
this._voiceStatus = "Voice startup failed. Try again or use text input.";
this._recognition = null;
this._render();
return false;
} }
this._startBrowserVoiceRecognition();
} }
async _startAssistSttRecognition() { async _startAssistSttRecognition() {
@@ -637,20 +643,20 @@ class OpenClawChatCard extends HTMLElement {
this._voiceStatus = this._voiceStatus =
"Continuous voice mode currently requires browser speech. Switch voice provider to browser or disable continuous mode."; "Continuous voice mode currently requires browser speech. Switch voice provider to browser or disable continuous mode.";
this._render(); this._render();
return; return false;
} }
if (!navigator.mediaDevices?.getUserMedia) { if (!navigator.mediaDevices?.getUserMedia) {
this._voiceStatus = "Microphone capture not supported by this browser."; this._voiceStatus = "Microphone capture not supported by this browser.";
this._render(); this._render();
return; return false;
} }
if (!this._preferredAssistSttEngine) { if (!this._preferredAssistSttEngine) {
this._voiceStatus = this._voiceStatus =
"No Assist STT engine found in preferred voice pipeline. Configure Voice settings first."; "No Assist STT engine found in preferred voice pipeline. Configure Voice settings first.";
this._render(); this._render();
return; return false;
} }
try { try {
@@ -737,12 +743,14 @@ class OpenClawChatCard extends HTMLElement {
this._assistAutoStopTimer = setTimeout(() => { this._assistAutoStopTimer = setTimeout(() => {
this._stopAssistSttRecognition(true); this._stopAssistSttRecognition(true);
}, 4500); }, 4500);
return true;
} catch (err) { } catch (err) {
console.error("OpenClaw: failed to start Assist STT recording", err); console.error("OpenClaw: failed to start Assist STT recording", err);
this._voiceStatus = "Could not start microphone recording for Assist STT."; this._voiceStatus = "Could not start microphone recording for Assist STT.";
this._assistRecordingActive = false; this._assistRecordingActive = false;
this._recognition = null; this._recognition = null;
this._render(); this._render();
return false;
} }
} }
@@ -1021,13 +1029,14 @@ class OpenClawChatCard extends HTMLElement {
this._voiceStatus = this._voiceStatus =
"Voice input disabled on Brave by default due browser SpeechRecognition network failures. Use Chrome/Edge, or set allow_brave_webspeech: true in card config to force-enable experimental mode."; "Voice input disabled on Brave by default due browser SpeechRecognition network failures. Use Chrome/Edge, or set allow_brave_webspeech: true in card config to force-enable experimental mode.";
this._render(); this._render();
return; return false;
} }
if (!("webkitSpeechRecognition" in window) && !("SpeechRecognition" in window)) { if (!("webkitSpeechRecognition" in window) && !("SpeechRecognition" in window)) {
console.warn("OpenClaw: Speech recognition not supported in this browser"); console.warn("OpenClaw: Speech recognition not supported in this browser");
this._voiceStatus = "Browser speech recognition is not supported here.";
this._render(); this._render();
return; return false;
} }
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
@@ -1153,8 +1162,17 @@ class OpenClawChatCard extends HTMLElement {
} }
}; };
try {
this._recognition.start(); this._recognition.start();
this._render(); this._render();
return true;
} catch (err) {
console.error("OpenClaw: failed to start SpeechRecognition", err);
this._voiceStatus = "Failed to start browser speech recognition.";
this._recognition = null;
this._render();
return false;
}
} }
_stopVoiceRecognition() { _stopVoiceRecognition() {
@@ -1209,7 +1227,10 @@ class OpenClawChatCard extends HTMLElement {
await this._loadIntegrationSettings(false); await this._loadIntegrationSettings(false);
this._isVoiceMode = !this._isVoiceMode; this._isVoiceMode = !this._isVoiceMode;
if (this._isVoiceMode) { if (this._isVoiceMode) {
this._startVoiceRecognition(); const started = await this._startVoiceRecognition();
if (!started) {
this._isVoiceMode = false;
}
} else { } else {
this._stopVoiceRecognition(); this._stopVoiceRecognition();
} }
@@ -1654,13 +1675,24 @@ class OpenClawChatCard extends HTMLElement {
this._stopVoiceRecognition(); this._stopVoiceRecognition();
this._render(); this._render();
} else { } else {
this._startVoiceRecognition(); this._startVoiceRecognition().catch((err) => {
console.error("OpenClaw: voice start failed", err);
this._voiceStatus = "Voice startup failed.";
this._render();
});
} }
}); });
} }
if (voiceModeBtn) { if (voiceModeBtn) {
voiceModeBtn.addEventListener("click", () => this._toggleVoiceMode()); voiceModeBtn.addEventListener("click", () => {
this._toggleVoiceMode().catch((err) => {
console.error("OpenClaw: voice mode toggle failed", err);
this._isVoiceMode = false;
this._voiceStatus = "Voice mode failed to start.";
this._render();
});
});
} }
const clearBtn = this.shadowRoot.getElementById("clear-btn"); const clearBtn = this.shadowRoot.getElementById("clear-btn");
+1 -1
View File
@@ -1,7 +1,7 @@
(async () => { (async () => {
try { try {
if (!customElements.get("openclaw-chat-card")) { if (!customElements.get("openclaw-chat-card")) {
const src = "/openclaw/openclaw-chat-card.js?v=0.1.44"; const src = "/openclaw/openclaw-chat-card.js?v=0.1.45";
console.info("OpenClaw loader importing", src); console.info("OpenClaw loader importing", src);
await import(src); await import(src);
} }