Update version to 0.1.42 and add browser voice language integration with fixes for voice replies and configuration options

This commit is contained in:
techartdev
2026-02-21 00:12:15 +02:00
parent 722c6752df
commit f070299c26
12 changed files with 155 additions and 70 deletions
+9
View File
@@ -2,6 +2,15 @@
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.42] - 2026-02-21
### Added
- Added integration option `browser_voice_language` (shown when `voice_provider` is `browser`) to explicitly control browser STT/TTS language.
### Fixed
- Browser voice provider now applies integration-configured browser voice language for both listening and spoken replies.
- Voice replies are no longer spoken for one-shot/manual voice sends unless continuous voice mode is actively running.
## [0.1.41] - 2026-02-20 ## [0.1.41] - 2026-02-20
### Fixed ### Fixed
+7 -1
View File
@@ -58,6 +58,7 @@ from .const import (
CONF_WAKE_WORD, CONF_WAKE_WORD,
CONF_WAKE_WORD_ENABLED, CONF_WAKE_WORD_ENABLED,
CONF_ALLOW_BRAVE_WEBSPEECH, CONF_ALLOW_BRAVE_WEBSPEECH,
CONF_BROWSER_VOICE_LANGUAGE,
CONF_VOICE_PROVIDER, CONF_VOICE_PROVIDER,
CONTEXT_STRATEGY_TRUNCATE, CONTEXT_STRATEGY_TRUNCATE,
DEFAULT_CONTEXT_MAX_CHARS, DEFAULT_CONTEXT_MAX_CHARS,
@@ -67,6 +68,7 @@ from .const import (
DEFAULT_WAKE_WORD, DEFAULT_WAKE_WORD,
DEFAULT_WAKE_WORD_ENABLED, DEFAULT_WAKE_WORD_ENABLED,
DEFAULT_ALLOW_BRAVE_WEBSPEECH, DEFAULT_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_BROWSER_VOICE_LANGUAGE,
DEFAULT_VOICE_PROVIDER, DEFAULT_VOICE_PROVIDER,
DOMAIN, DOMAIN,
EVENT_MESSAGE_RECEIVED, EVENT_MESSAGE_RECEIVED,
@@ -90,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.41" _CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.42"
OpenClawConfigEntry = ConfigEntry OpenClawConfigEntry = ConfigEntry
@@ -808,6 +810,10 @@ def _async_register_websocket_api(hass: HomeAssistant) -> None:
CONF_VOICE_PROVIDER, CONF_VOICE_PROVIDER,
DEFAULT_VOICE_PROVIDER, DEFAULT_VOICE_PROVIDER,
), ),
CONF_BROWSER_VOICE_LANGUAGE: options.get(
CONF_BROWSER_VOICE_LANGUAGE,
DEFAULT_BROWSER_VOICE_LANGUAGE,
),
"language": hass.config.language, "language": hass.config.language,
}, },
) )
+18 -8
View File
@@ -45,7 +45,9 @@ from .const import (
CONF_WAKE_WORD, CONF_WAKE_WORD,
CONF_WAKE_WORD_ENABLED, CONF_WAKE_WORD_ENABLED,
CONF_ALLOW_BRAVE_WEBSPEECH, CONF_ALLOW_BRAVE_WEBSPEECH,
CONF_BROWSER_VOICE_LANGUAGE,
CONF_VOICE_PROVIDER, CONF_VOICE_PROVIDER,
BROWSER_VOICE_LANGUAGES,
CONTEXT_STRATEGY_CLEAR, CONTEXT_STRATEGY_CLEAR,
CONTEXT_STRATEGY_TRUNCATE, CONTEXT_STRATEGY_TRUNCATE,
DEFAULT_GATEWAY_HOST, DEFAULT_GATEWAY_HOST,
@@ -57,6 +59,7 @@ from .const import (
DEFAULT_WAKE_WORD, DEFAULT_WAKE_WORD,
DEFAULT_WAKE_WORD_ENABLED, DEFAULT_WAKE_WORD_ENABLED,
DEFAULT_ALLOW_BRAVE_WEBSPEECH, DEFAULT_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_BROWSER_VOICE_LANGUAGE,
DEFAULT_VOICE_PROVIDER, DEFAULT_VOICE_PROVIDER,
DOMAIN, DOMAIN,
OPENCLAW_CONFIG_REL_PATH, OPENCLAW_CONFIG_REL_PATH,
@@ -414,10 +417,9 @@ class OpenClawOptionsFlow(OptionsFlow):
return self.async_create_entry(title="", data=user_input) return self.async_create_entry(title="", data=user_input)
options = self._config_entry.options options = self._config_entry.options
return self.async_show_form( selected_provider = options.get(CONF_VOICE_PROVIDER, DEFAULT_VOICE_PROVIDER)
step_id="init",
data_schema=vol.Schema( schema: dict[Any, Any] = {
{
vol.Optional( vol.Optional(
CONF_INCLUDE_EXPOSED_CONTEXT, CONF_INCLUDE_EXPOSED_CONTEXT,
default=options.get( default=options.get(
@@ -469,11 +471,19 @@ class OpenClawOptionsFlow(OptionsFlow):
): bool, ): bool,
vol.Optional( vol.Optional(
CONF_VOICE_PROVIDER, CONF_VOICE_PROVIDER,
default=options.get( default=selected_provider,
CONF_VOICE_PROVIDER,
DEFAULT_VOICE_PROVIDER,
),
): vol.In(["browser", "assist_stt"]), ): vol.In(["browser", "assist_stt"]),
} }
if selected_provider == "browser":
schema[
vol.Optional(
CONF_BROWSER_VOICE_LANGUAGE,
default=options.get(
CONF_BROWSER_VOICE_LANGUAGE,
DEFAULT_BROWSER_VOICE_LANGUAGE,
), ),
) )
] = vol.In(BROWSER_VOICE_LANGUAGES)
return self.async_show_form(step_id="init", data_schema=vol.Schema(schema))
+39
View File
@@ -32,6 +32,7 @@ CONF_WAKE_WORD_ENABLED = "wake_word_enabled"
CONF_WAKE_WORD = "wake_word" CONF_WAKE_WORD = "wake_word"
CONF_ALLOW_BRAVE_WEBSPEECH = "allow_brave_webspeech" CONF_ALLOW_BRAVE_WEBSPEECH = "allow_brave_webspeech"
CONF_VOICE_PROVIDER = "voice_provider" CONF_VOICE_PROVIDER = "voice_provider"
CONF_BROWSER_VOICE_LANGUAGE = "browser_voice_language"
DEFAULT_INCLUDE_EXPOSED_CONTEXT = True DEFAULT_INCLUDE_EXPOSED_CONTEXT = True
DEFAULT_CONTEXT_MAX_CHARS = 13000 DEFAULT_CONTEXT_MAX_CHARS = 13000
@@ -41,6 +42,44 @@ DEFAULT_WAKE_WORD_ENABLED = False
DEFAULT_WAKE_WORD = "hey openclaw" DEFAULT_WAKE_WORD = "hey openclaw"
DEFAULT_ALLOW_BRAVE_WEBSPEECH = False DEFAULT_ALLOW_BRAVE_WEBSPEECH = False
DEFAULT_VOICE_PROVIDER = "browser" DEFAULT_VOICE_PROVIDER = "browser"
DEFAULT_BROWSER_VOICE_LANGUAGE = "auto"
BROWSER_VOICE_LANGUAGES: tuple[str, ...] = (
"auto",
"bg-BG",
"en-US",
"en-GB",
"de-DE",
"fr-FR",
"es-ES",
"it-IT",
"pt-PT",
"pt-BR",
"ru-RU",
"nl-NL",
"pl-PL",
"tr-TR",
"uk-UA",
"cs-CZ",
"ro-RO",
"el-GR",
"sv-SE",
"no-NO",
"da-DK",
"fi-FI",
"hu-HU",
"sk-SK",
"sl-SI",
"hr-HR",
"sr-RS",
"ja-JP",
"ko-KR",
"zh-CN",
"zh-TW",
"ar-SA",
"he-IL",
"hi-IN",
)
CONTEXT_STRATEGY_TRUNCATE = "truncate" CONTEXT_STRATEGY_TRUNCATE = "truncate"
CONTEXT_STRATEGY_CLEAR = "clear" CONTEXT_STRATEGY_CLEAR = "clear"
+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.41", "version": "0.1.42",
"dependencies": ["conversation"], "dependencies": ["conversation"],
"after_dependencies": ["hassio", "lovelace"] "after_dependencies": ["hassio", "lovelace"]
} }
+2 -1
View File
@@ -44,7 +44,8 @@
"wake_word_enabled": "Require wake word", "wake_word_enabled": "Require wake word",
"wake_word": "Wake word", "wake_word": "Wake word",
"allow_brave_webspeech": "Allow Web Speech in Brave (experimental)", "allow_brave_webspeech": "Allow Web Speech in Brave (experimental)",
"voice_provider": "Voice input provider (browser or HA STT)" "voice_provider": "Voice input provider (browser or HA STT)",
"browser_voice_language": "Browser voice language"
} }
} }
} }
@@ -44,7 +44,8 @@
"wake_word_enabled": "Require wake word", "wake_word_enabled": "Require wake word",
"wake_word": "Wake word", "wake_word": "Wake word",
"allow_brave_webspeech": "Allow Web Speech in Brave (experimental)", "allow_brave_webspeech": "Allow Web Speech in Brave (experimental)",
"voice_provider": "Voice input provider (browser or HA STT)" "voice_provider": "Voice input provider (browser or HA STT)",
"browser_voice_language": "Browser voice language"
} }
} }
} }
@@ -13,7 +13,7 @@
* + subscribes to openclaw_message_received events. * + subscribes to openclaw_message_received events.
*/ */
const CARD_VERSION = "0.3.5"; const CARD_VERSION = "0.3.6";
// 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;
@@ -68,6 +68,7 @@ class OpenClawChatCard extends HTMLElement {
this._voiceNetworkErrorCount = 0; this._voiceNetworkErrorCount = 0;
this._pendingResponses = 0; this._pendingResponses = 0;
this._speechLangOverride = null; this._speechLangOverride = null;
this._integrationBrowserVoiceLanguage = null;
this._integrationVoiceLanguage = null; this._integrationVoiceLanguage = null;
this._integrationTtsLanguage = null; this._integrationTtsLanguage = null;
this._allowBraveWebSpeechIntegration = false; this._allowBraveWebSpeechIntegration = false;
@@ -229,7 +230,7 @@ class OpenClawChatCard extends HTMLElement {
this._scrollToBottom(); this._scrollToBottom();
// In voice mode, speak the response // In voice mode, speak the response
if (this._isVoiceMode && data.message) { if (this._isVoiceMode && this._recognition && data.message) {
this._speak(data.message); this._speak(data.message);
} }
} }
@@ -354,6 +355,10 @@ class OpenClawChatCard extends HTMLElement {
this._allowBraveWebSpeechIntegration = !!result?.allow_brave_webspeech; this._allowBraveWebSpeechIntegration = !!result?.allow_brave_webspeech;
this._voiceProviderIntegration = this._voiceProviderIntegration =
result?.voice_provider === "assist_stt" ? "assist_stt" : "browser"; result?.voice_provider === "assist_stt" ? "assist_stt" : "browser";
this._integrationBrowserVoiceLanguage =
result?.browser_voice_language && result?.browser_voice_language !== "auto"
? this._normalizeSpeechLanguage(result.browser_voice_language)
: null;
this._integrationVoiceLanguage = result?.language this._integrationVoiceLanguage = result?.language
? this._normalizeSpeechLanguage(result.language) ? this._normalizeSpeechLanguage(result.language)
: null; : null;
@@ -544,6 +549,13 @@ class OpenClawChatCard extends HTMLElement {
} }
const configuredLang = this._config.voice_language; const configuredLang = this._config.voice_language;
if (configuredLang) {
return this._normalizeSpeechLanguage(configuredLang);
}
const provider = this._getVoiceProvider();
if (provider === "browser" && this._integrationBrowserVoiceLanguage) {
return this._normalizeSpeechLanguage(this._integrationBrowserVoiceLanguage);
}
const integrationLang = this._integrationVoiceLanguage; const integrationLang = this._integrationVoiceLanguage;
const hassLang = const hassLang =
this._hass?.locale?.language || this._hass?.selectedLanguage || this._hass?.language; this._hass?.locale?.language || this._hass?.selectedLanguage || this._hass?.language;
@@ -554,6 +566,13 @@ class OpenClawChatCard extends HTMLElement {
_getSpeechSynthesisLanguage() { _getSpeechSynthesisLanguage() {
const configuredLang = this._config.voice_language; const configuredLang = this._config.voice_language;
if (configuredLang) {
return this._normalizeSpeechLanguage(configuredLang);
}
const provider = this._getVoiceProvider();
if (provider === "browser" && this._integrationBrowserVoiceLanguage) {
return this._normalizeSpeechLanguage(this._integrationBrowserVoiceLanguage);
}
const preferred = const preferred =
configuredLang || configuredLang ||
this._integrationTtsLanguage || this._integrationTtsLanguage ||
+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.41"; const src = "/openclaw/openclaw-chat-card.js?v=0.1.42";
console.info("OpenClaw loader importing", src); console.info("OpenClaw loader importing", src);
await import(src); await import(src);
} }