voice mode fix attempt 1000

This commit is contained in:
techartdev
2026-02-20 19:23:49 +02:00
parent 98fa3a7518
commit f94a62ef7b
5 changed files with 42 additions and 8 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.23] - 2026-02-20
### Fixed
- Improved handling for repeated `SpeechRecognition` `network` failures in Brave-like browsers.
- Added clear in-card status when browser speech backend appears blocked, and stopped endless retry loops in that case.
- Kept automatic locale fallback retry for transient speech-service issues.
## [0.1.22] - 2026-02-20 ## [0.1.22] - 2026-02-20
### Fixed ### Fixed
+1 -1
View File
@@ -74,7 +74,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.22" _CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.23"
type OpenClawConfigEntry = ConfigEntry type 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.22", "version": "0.1.23",
"dependencies": ["conversation"], "dependencies": ["conversation"],
"after_dependencies": ["hassio", "lovelace"] "after_dependencies": ["hassio", "lovelace"]
} }
@@ -13,7 +13,7 @@
* + subscribes to openclaw_message_received events. * + subscribes to openclaw_message_received events.
*/ */
const CARD_VERSION = "0.2.2"; const CARD_VERSION = "0.2.3";
// Max time (ms) to show the thinking indicator before falling back to an error // Max time (ms) to show the thinking indicator before falling back to an error
const THINKING_TIMEOUT_MS = 120_000; const THINKING_TIMEOUT_MS = 120_000;
@@ -66,7 +66,9 @@ class OpenClawChatCard extends HTMLElement {
this._voiceStatus = ""; this._voiceStatus = "";
this._voiceRetryTimer = null; this._voiceRetryTimer = null;
this._voiceRetryCount = 0; this._voiceRetryCount = 0;
this._voiceNetworkErrorCount = 0;
this._speechLangOverride = null; this._speechLangOverride = null;
this._voiceBackendBlocked = false;
} }
// ── HA card interface ─────────────────────────────────────────────── // ── HA card interface ───────────────────────────────────────────────
@@ -442,7 +444,20 @@ class OpenClawChatCard extends HTMLElement {
return this._normalizeSpeechLanguage(preferred); return this._normalizeSpeechLanguage(preferred);
} }
_isLikelyBraveBrowser() {
const ua = (navigator.userAgent || "").toLowerCase();
const uaDataBrands = navigator.userAgentData?.brands || [];
const brandMatch = uaDataBrands.some((brand) =>
String(brand?.brand || "")
.toLowerCase()
.includes("brave")
);
return brandMatch || ua.includes("brave") || !!navigator.brave;
}
_startVoiceRecognition() { _startVoiceRecognition() {
this._voiceBackendBlocked = 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 = "Speech recognition not supported by this browser."; this._voiceStatus = "Speech recognition not supported by this browser.";
@@ -500,12 +515,22 @@ class OpenClawChatCard extends HTMLElement {
console.error("OpenClaw: Speech recognition error:", event.error); console.error("OpenClaw: Speech recognition error:", event.error);
const err = event?.error || "unknown"; const err = event?.error || "unknown";
if (err === "network") { if (err === "network") {
this._voiceStatus = this._voiceNetworkErrorCount += 1;
"Voice network error: browser speech service unavailable. Retrying with fallback locale…";
const browserLocale = this._normalizeSpeechLanguage(navigator.language || "en-US"); const browserLocale = this._normalizeSpeechLanguage(navigator.language || "en-US");
if (!this._speechLangOverride && browserLocale !== this._recognition.lang) { if (!this._speechLangOverride && browserLocale !== this._recognition.lang) {
this._speechLangOverride = browserLocale; this._speechLangOverride = browserLocale;
this._voiceStatus =
"Voice network error: browser speech service unavailable. Retrying with fallback locale…";
} else {
const braveLikely = this._isLikelyBraveBrowser();
if (braveLikely && this._voiceNetworkErrorCount >= 2) {
this._voiceBackendBlocked = true;
this._voiceStatus =
"Brave speech backend blocked (network error). Voice input is unavailable in this browser session. Use Chrome/Edge for voice, or continue with text input.";
} else {
this._voiceStatus =
"Voice network error: browser speech service unavailable. Retrying…";
}
} }
} else if (err === "not-allowed") { } else if (err === "not-allowed") {
this._voiceStatus = "Microphone access denied. Allow mic permission for this site."; this._voiceStatus = "Microphone access denied. Allow mic permission for this site.";
@@ -515,7 +540,7 @@ class OpenClawChatCard extends HTMLElement {
this._voiceStatus = `Voice error: ${err}`; this._voiceStatus = `Voice error: ${err}`;
} }
if (["network", "audio-capture", "no-speech"].includes(err)) { if (["network", "audio-capture", "no-speech"].includes(err) && !this._voiceBackendBlocked) {
this._scheduleVoiceRetry(); this._scheduleVoiceRetry();
} }
this._render(); this._render();
@@ -549,11 +574,13 @@ class OpenClawChatCard extends HTMLElement {
this._voiceRetryTimer = null; this._voiceRetryTimer = null;
} }
this._voiceRetryCount = 0; this._voiceRetryCount = 0;
this._voiceNetworkErrorCount = 0;
this._isVoiceMode = false; this._isVoiceMode = false;
this._voiceStatus = ""; this._voiceStatus = "";
} }
_scheduleVoiceRetry() { _scheduleVoiceRetry() {
if (this._voiceBackendBlocked) return;
if (!this._isVoiceMode && !this._speechLangOverride) return; if (!this._isVoiceMode && !this._speechLangOverride) return;
if (this._voiceRetryCount >= 6) { if (this._voiceRetryCount >= 6) {
this._voiceStatus = this._voiceStatus =
+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.22"; const src = "/openclaw/openclaw-chat-card.js?v=0.1.23";
console.info("OpenClaw loader importing", src); console.info("OpenClaw loader importing", src);
await import(src); await import(src);
} }