Update version to 0.1.47 and improve TTS fallback handling and voice-mode responsiveness
This commit is contained in:
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
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.47] - 2026-02-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Improved continuous voice-mode responsiveness by finalizing recognition on speech end, reducing delay before sending spoken messages.
|
||||||
|
- Improved TTS fallback order: when no matching browser language voice is available, the card now tries Home Assistant TTS first before browser default-language fallback.
|
||||||
|
|
||||||
## [0.1.46] - 2026-02-21
|
## [0.1.46] - 2026-02-21
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -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.46"
|
_CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.47"
|
||||||
|
|
||||||
OpenClawConfigEntry = ConfigEntry
|
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.46",
|
"version": "0.1.47",
|
||||||
"dependencies": ["conversation"],
|
"dependencies": ["conversation"],
|
||||||
"after_dependencies": ["hassio", "lovelace"]
|
"after_dependencies": ["hassio", "lovelace"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1260,7 +1260,6 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
const fallbackLanguage = this._normalizeSpeechLanguage(
|
const fallbackLanguage = this._normalizeSpeechLanguage(
|
||||||
this._hass?.locale?.language || this._hass?.selectedLanguage || this._hass?.language || navigator.language || "en-US"
|
this._hass?.locale?.language || this._hass?.selectedLanguage || this._hass?.language || navigator.language || "en-US"
|
||||||
);
|
);
|
||||||
let attemptedFallback = false;
|
|
||||||
|
|
||||||
const pickVoice = (targetLanguage, voices) => {
|
const pickVoice = (targetLanguage, voices) => {
|
||||||
const normalizedTarget = String(targetLanguage || "").toLowerCase();
|
const normalizedTarget = String(targetLanguage || "").toLowerCase();
|
||||||
@@ -1297,6 +1296,7 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
const speakNow = (targetLanguage, allowFallback = true) => {
|
const speakNow = (targetLanguage, allowFallback = true) => {
|
||||||
const utterance = new SpeechSynthesisUtterance(plain);
|
const utterance = new SpeechSynthesisUtterance(plain);
|
||||||
utterance.lang = targetLanguage;
|
utterance.lang = targetLanguage;
|
||||||
|
let preSpeakHaFallbackAttempted = false;
|
||||||
|
|
||||||
const voices = speechSynthesis.getVoices() || [];
|
const voices = speechSynthesis.getVoices() || [];
|
||||||
if (voices.length) {
|
if (voices.length) {
|
||||||
@@ -1307,26 +1307,63 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
if (selection.lang) {
|
if (selection.lang) {
|
||||||
utterance.lang = selection.lang;
|
utterance.lang = selection.lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!selection.matched && allowFallback) {
|
||||||
|
preSpeakHaFallbackAttempted = true;
|
||||||
|
this._voiceStatus = "No matching browser TTS voice, trying Home Assistant TTS…";
|
||||||
|
this._render();
|
||||||
|
this._speakViaHomeAssistantTts(plain, targetLanguage).then((ok) => {
|
||||||
|
if (ok) {
|
||||||
|
this._voiceStatus = "";
|
||||||
|
this._render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._voiceStatus =
|
||||||
|
"Home Assistant TTS unavailable; using browser default voice as fallback.";
|
||||||
|
this._render();
|
||||||
|
try {
|
||||||
|
speechSynthesis.cancel();
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
speechSynthesis.speak(utterance);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utterance.onerror = (event) => {
|
utterance.onerror = (event) => {
|
||||||
if (allowFallback && !attemptedFallback && targetLanguage !== fallbackLanguage) {
|
if (!preSpeakHaFallbackAttempted) {
|
||||||
attemptedFallback = true;
|
this._voiceStatus = "Browser TTS failed, trying Home Assistant TTS…";
|
||||||
|
this._render();
|
||||||
|
this._speakViaHomeAssistantTts(plain, targetLanguage).then((ok) => {
|
||||||
|
if (ok) {
|
||||||
|
this._voiceStatus = "";
|
||||||
|
this._render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowFallback && targetLanguage !== fallbackLanguage) {
|
||||||
|
speakNow(fallbackLanguage, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reason = event?.error ? ` (${event.error})` : "";
|
||||||
|
this._voiceStatus = `TTS error for language ${targetLanguage}${reason}`;
|
||||||
|
this._render();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowFallback && targetLanguage !== fallbackLanguage) {
|
||||||
speakNow(fallbackLanguage, false);
|
speakNow(fallbackLanguage, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._voiceStatus = "Browser TTS failed, trying Home Assistant TTS…";
|
|
||||||
|
const reason = event?.error ? ` (${event.error})` : "";
|
||||||
|
this._voiceStatus = `TTS error for language ${targetLanguage}${reason}`;
|
||||||
this._render();
|
this._render();
|
||||||
this._speakViaHomeAssistantTts(plain, targetLanguage).then((ok) => {
|
|
||||||
if (ok) {
|
|
||||||
this._voiceStatus = "";
|
|
||||||
this._render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const reason = event?.error ? ` (${event.error})` : "";
|
|
||||||
this._voiceStatus = `TTS error for language ${targetLanguage}${reason}`;
|
|
||||||
this._render();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -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.46";
|
const src = "/openclaw/openclaw-chat-card.js?v=0.1.47";
|
||||||
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