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
+7 -1
View File
@@ -58,6 +58,7 @@ from .const import (
CONF_WAKE_WORD,
CONF_WAKE_WORD_ENABLED,
CONF_ALLOW_BRAVE_WEBSPEECH,
CONF_BROWSER_VOICE_LANGUAGE,
CONF_VOICE_PROVIDER,
CONTEXT_STRATEGY_TRUNCATE,
DEFAULT_CONTEXT_MAX_CHARS,
@@ -67,6 +68,7 @@ from .const import (
DEFAULT_WAKE_WORD,
DEFAULT_WAKE_WORD_ENABLED,
DEFAULT_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_BROWSER_VOICE_LANGUAGE,
DEFAULT_VOICE_PROVIDER,
DOMAIN,
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)
_CARD_STATIC_URL = f"/openclaw/{_CARD_FILENAME}"
# 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
@@ -808,6 +810,10 @@ def _async_register_websocket_api(hass: HomeAssistant) -> None:
CONF_VOICE_PROVIDER,
DEFAULT_VOICE_PROVIDER,
),
CONF_BROWSER_VOICE_LANGUAGE: options.get(
CONF_BROWSER_VOICE_LANGUAGE,
DEFAULT_BROWSER_VOICE_LANGUAGE,
),
"language": hass.config.language,
},
)
+73 -63
View File
@@ -45,7 +45,9 @@ from .const import (
CONF_WAKE_WORD,
CONF_WAKE_WORD_ENABLED,
CONF_ALLOW_BRAVE_WEBSPEECH,
CONF_BROWSER_VOICE_LANGUAGE,
CONF_VOICE_PROVIDER,
BROWSER_VOICE_LANGUAGES,
CONTEXT_STRATEGY_CLEAR,
CONTEXT_STRATEGY_TRUNCATE,
DEFAULT_GATEWAY_HOST,
@@ -57,6 +59,7 @@ from .const import (
DEFAULT_WAKE_WORD,
DEFAULT_WAKE_WORD_ENABLED,
DEFAULT_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_BROWSER_VOICE_LANGUAGE,
DEFAULT_VOICE_PROVIDER,
DOMAIN,
OPENCLAW_CONFIG_REL_PATH,
@@ -414,66 +417,73 @@ class OpenClawOptionsFlow(OptionsFlow):
return self.async_create_entry(title="", data=user_input)
options = self._config_entry.options
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_INCLUDE_EXPOSED_CONTEXT,
default=options.get(
CONF_INCLUDE_EXPOSED_CONTEXT,
DEFAULT_INCLUDE_EXPOSED_CONTEXT,
),
): bool,
vol.Optional(
CONF_CONTEXT_MAX_CHARS,
default=options.get(
CONF_CONTEXT_MAX_CHARS,
DEFAULT_CONTEXT_MAX_CHARS,
),
): vol.All(int, vol.Range(min=1000, max=200000)),
vol.Optional(
CONF_CONTEXT_STRATEGY,
default=options.get(
CONF_CONTEXT_STRATEGY,
DEFAULT_CONTEXT_STRATEGY,
),
): vol.In([CONTEXT_STRATEGY_TRUNCATE, CONTEXT_STRATEGY_CLEAR]),
vol.Optional(
CONF_ENABLE_TOOL_CALLS,
default=options.get(
CONF_ENABLE_TOOL_CALLS,
DEFAULT_ENABLE_TOOL_CALLS,
),
): bool,
vol.Optional(
CONF_WAKE_WORD_ENABLED,
default=options.get(
CONF_WAKE_WORD_ENABLED,
DEFAULT_WAKE_WORD_ENABLED,
),
): bool,
vol.Optional(
CONF_WAKE_WORD,
default=options.get(
CONF_WAKE_WORD,
DEFAULT_WAKE_WORD,
),
): str,
vol.Optional(
CONF_ALLOW_BRAVE_WEBSPEECH,
default=options.get(
CONF_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_ALLOW_BRAVE_WEBSPEECH,
),
): bool,
vol.Optional(
CONF_VOICE_PROVIDER,
default=options.get(
CONF_VOICE_PROVIDER,
DEFAULT_VOICE_PROVIDER,
),
): vol.In(["browser", "assist_stt"]),
}
),
)
selected_provider = options.get(CONF_VOICE_PROVIDER, DEFAULT_VOICE_PROVIDER)
schema: dict[Any, Any] = {
vol.Optional(
CONF_INCLUDE_EXPOSED_CONTEXT,
default=options.get(
CONF_INCLUDE_EXPOSED_CONTEXT,
DEFAULT_INCLUDE_EXPOSED_CONTEXT,
),
): bool,
vol.Optional(
CONF_CONTEXT_MAX_CHARS,
default=options.get(
CONF_CONTEXT_MAX_CHARS,
DEFAULT_CONTEXT_MAX_CHARS,
),
): vol.All(int, vol.Range(min=1000, max=200000)),
vol.Optional(
CONF_CONTEXT_STRATEGY,
default=options.get(
CONF_CONTEXT_STRATEGY,
DEFAULT_CONTEXT_STRATEGY,
),
): vol.In([CONTEXT_STRATEGY_TRUNCATE, CONTEXT_STRATEGY_CLEAR]),
vol.Optional(
CONF_ENABLE_TOOL_CALLS,
default=options.get(
CONF_ENABLE_TOOL_CALLS,
DEFAULT_ENABLE_TOOL_CALLS,
),
): bool,
vol.Optional(
CONF_WAKE_WORD_ENABLED,
default=options.get(
CONF_WAKE_WORD_ENABLED,
DEFAULT_WAKE_WORD_ENABLED,
),
): bool,
vol.Optional(
CONF_WAKE_WORD,
default=options.get(
CONF_WAKE_WORD,
DEFAULT_WAKE_WORD,
),
): str,
vol.Optional(
CONF_ALLOW_BRAVE_WEBSPEECH,
default=options.get(
CONF_ALLOW_BRAVE_WEBSPEECH,
DEFAULT_ALLOW_BRAVE_WEBSPEECH,
),
): bool,
vol.Optional(
CONF_VOICE_PROVIDER,
default=selected_provider,
): 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_ALLOW_BRAVE_WEBSPEECH = "allow_brave_webspeech"
CONF_VOICE_PROVIDER = "voice_provider"
CONF_BROWSER_VOICE_LANGUAGE = "browser_voice_language"
DEFAULT_INCLUDE_EXPOSED_CONTEXT = True
DEFAULT_CONTEXT_MAX_CHARS = 13000
@@ -41,6 +42,44 @@ DEFAULT_WAKE_WORD_ENABLED = False
DEFAULT_WAKE_WORD = "hey openclaw"
DEFAULT_ALLOW_BRAVE_WEBSPEECH = False
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_CLEAR = "clear"
+1 -1
View File
@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
"requirements": [],
"version": "0.1.41",
"version": "0.1.42",
"dependencies": ["conversation"],
"after_dependencies": ["hassio", "lovelace"]
}
+2 -1
View File
@@ -44,7 +44,8 @@
"wake_word_enabled": "Require wake word",
"wake_word": "Wake word",
"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": "Wake word",
"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.
*/
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
const THINKING_TIMEOUT_MS = 120_000;
@@ -68,6 +68,7 @@ class OpenClawChatCard extends HTMLElement {
this._voiceNetworkErrorCount = 0;
this._pendingResponses = 0;
this._speechLangOverride = null;
this._integrationBrowserVoiceLanguage = null;
this._integrationVoiceLanguage = null;
this._integrationTtsLanguage = null;
this._allowBraveWebSpeechIntegration = false;
@@ -229,7 +230,7 @@ class OpenClawChatCard extends HTMLElement {
this._scrollToBottom();
// In voice mode, speak the response
if (this._isVoiceMode && data.message) {
if (this._isVoiceMode && this._recognition && data.message) {
this._speak(data.message);
}
}
@@ -354,6 +355,10 @@ class OpenClawChatCard extends HTMLElement {
this._allowBraveWebSpeechIntegration = !!result?.allow_brave_webspeech;
this._voiceProviderIntegration =
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._normalizeSpeechLanguage(result.language)
: null;
@@ -544,6 +549,13 @@ class OpenClawChatCard extends HTMLElement {
}
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 hassLang =
this._hass?.locale?.language || this._hass?.selectedLanguage || this._hass?.language;
@@ -554,6 +566,13 @@ class OpenClawChatCard extends HTMLElement {
_getSpeechSynthesisLanguage() {
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 =
configuredLang ||
this._integrationTtsLanguage ||