Update version to 0.1.50 and enhance voice behavior, chat consistency, and auto-scroll functionality

This commit is contained in:
techartdev
2026-02-21 19:07:53 +02:00
parent 77b64fd785
commit bee2fd5a45
5 changed files with 59 additions and 6 deletions
+5
View File
@@ -2,6 +2,11 @@
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.50] - 2026-02-21
### Fixed
- Improved Android Home Assistant app voice behavior by preferring Home Assistant TTS first and handling browser-TTS-unavailable cases correctly.
## [0.1.49] - 2026-02-21 ## [0.1.49] - 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.49" _CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.50"
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.49", "version": "0.1.50",
"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.3.8"; const CARD_VERSION = "0.3.9";
// 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;
@@ -641,6 +641,11 @@ class OpenClawChatCard extends HTMLElement {
return brandMatch || ua.includes("brave") || !!navigator.brave; return brandMatch || ua.includes("brave") || !!navigator.brave;
} }
_isHomeAssistantAndroidApp() {
const ua = (navigator.userAgent || "").toLowerCase();
return ua.includes("home assistant") && ua.includes("android");
}
_getVoiceProvider() { _getVoiceProvider() {
const configured = this._config.voice_provider; const configured = this._config.voice_provider;
if (configured === "assist_stt" || configured === "browser") { if (configured === "assist_stt" || configured === "browser") {
@@ -1218,6 +1223,7 @@ class OpenClawChatCard extends HTMLElement {
if (this._voiceIdleRestartTimer) { if (this._voiceIdleRestartTimer) {
clearTimeout(this._voiceIdleRestartTimer); clearTimeout(this._voiceIdleRestartTimer);
} }
const idleRestartDelayMs = this._isHomeAssistantAndroidApp() ? 3500 : 1500;
this._voiceIdleRestartTimer = setTimeout(() => { this._voiceIdleRestartTimer = setTimeout(() => {
this._voiceIdleRestartTimer = null; this._voiceIdleRestartTimer = null;
if (!this._isVoiceMode || !this._recognition) { if (!this._isVoiceMode || !this._recognition) {
@@ -1228,7 +1234,7 @@ class OpenClawChatCard extends HTMLElement {
} catch (e) { } catch (e) {
// Ignore — may already be started // Ignore — may already be started
} }
}, 1500); }, idleRestartDelayMs);
return; return;
} }
@@ -1348,8 +1354,10 @@ class OpenClawChatCard extends HTMLElement {
} }
_speak(text) { _speak(text) {
if (!("speechSynthesis" in window)) return; const hasBrowserTts = "speechSynthesis" in window;
this._pauseVoiceInputForTts(); this._pauseVoiceInputForTts();
this._voiceStatus = "Speaking…";
this._render();
// Strip markdown for TTS // Strip markdown for TTS
const plain = text.replace(/[*_`#\[\]()]/g, ""); const plain = text.replace(/[*_`#\[\]()]/g, "");
const preferredLanguage = this._getSpeechSynthesisLanguage(); const preferredLanguage = this._getSpeechSynthesisLanguage();
@@ -1357,6 +1365,25 @@ class OpenClawChatCard extends HTMLElement {
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"
); );
if (!hasBrowserTts) {
this._voiceStatus = "Browser TTS unavailable, trying Home Assistant TTS…";
this._render();
this._speakViaHomeAssistantTts(plain, preferredLanguage).then((ok) => {
if (ok) {
this._voiceStatus = "";
this._render();
this._resumeVoiceInputAfterTts();
return;
}
const reason = this._lastHaTtsAttempt ? ` (${this._lastHaTtsAttempt})` : "";
this._voiceStatus = `TTS unavailable${reason}`;
this._render();
this._resumeVoiceInputAfterTts();
});
return;
}
const pickVoice = (targetLanguage, voices) => { const pickVoice = (targetLanguage, voices) => {
const normalizedTarget = String(targetLanguage || "").toLowerCase(); const normalizedTarget = String(targetLanguage || "").toLowerCase();
const targetBase = normalizedTarget.split("-")[0]; const targetBase = normalizedTarget.split("-")[0];
@@ -1468,6 +1495,8 @@ class OpenClawChatCard extends HTMLElement {
}; };
utterance.onend = () => { utterance.onend = () => {
this._voiceStatus = "";
this._render();
this._resumeVoiceInputAfterTts(); this._resumeVoiceInputAfterTts();
}; };
@@ -1479,6 +1508,25 @@ class OpenClawChatCard extends HTMLElement {
speechSynthesis.speak(utterance); speechSynthesis.speak(utterance);
}; };
if (this._isHomeAssistantAndroidApp()) {
this._voiceStatus = "Speaking with Home Assistant TTS…";
this._render();
this._speakViaHomeAssistantTts(plain, preferredLanguage).then((ok) => {
if (ok) {
this._voiceStatus = "";
this._render();
this._resumeVoiceInputAfterTts();
return;
}
const reason = this._lastHaTtsAttempt ? ` (${this._lastHaTtsAttempt})` : "";
this._voiceStatus = `Home Assistant TTS unavailable${reason}; trying browser TTS…`;
this._render();
speakNow(preferredLanguage);
});
return;
}
const loadedVoices = speechSynthesis.getVoices() || []; const loadedVoices = speechSynthesis.getVoices() || [];
if (loadedVoices.length) { if (loadedVoices.length) {
speakNow(preferredLanguage); speakNow(preferredLanguage);
+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.49"; const src = "/openclaw/openclaw-chat-card.js?v=0.1.50";
console.info("OpenClaw loader importing", src); console.info("OpenClaw loader importing", src);
await import(src); await import(src);
} }