voice mode fix attempt 1000
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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,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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user