fix voice language selection from voice settings
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.29] - 2026-02-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Voice language selection now prioritizes the preferred Assist pipeline language (`assist_pipeline/pipeline/list`) instead of only using Home Assistant UI language.
|
||||||
|
- Added separate TTS language resolution so spoken replies follow Assist pipeline TTS language when available.
|
||||||
|
- Retained safe fallbacks to integration/UI/browser language when Assist pipeline data is unavailable.
|
||||||
|
|
||||||
## [0.1.28] - 2026-02-20
|
## [0.1.28] - 2026-02-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -76,7 +76,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.28"
|
_CARD_URL = f"{_CARD_STATIC_URL}?v=0.1.29"
|
||||||
|
|
||||||
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.28",
|
"version": "0.1.29",
|
||||||
"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.7";
|
const CARD_VERSION = "0.2.8";
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -70,6 +70,7 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
this._pendingResponses = 0;
|
this._pendingResponses = 0;
|
||||||
this._speechLangOverride = null;
|
this._speechLangOverride = null;
|
||||||
this._integrationVoiceLanguage = null;
|
this._integrationVoiceLanguage = null;
|
||||||
|
this._integrationTtsLanguage = null;
|
||||||
this._allowBraveWebSpeechIntegration = false;
|
this._allowBraveWebSpeechIntegration = false;
|
||||||
this._voiceBackendBlocked = false;
|
this._voiceBackendBlocked = false;
|
||||||
this._voiceStopRequested = false;
|
this._voiceStopRequested = false;
|
||||||
@@ -320,6 +321,37 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
? this._normalizeSpeechLanguage(result.language)
|
? this._normalizeSpeechLanguage(result.language)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let pipelineResult;
|
||||||
|
if (typeof this._hass.callWS === "function") {
|
||||||
|
pipelineResult = await this._hass.callWS({ type: "assist_pipeline/pipeline/list" });
|
||||||
|
} else {
|
||||||
|
pipelineResult = await this._hass.connection.sendMessagePromise({
|
||||||
|
type: "assist_pipeline/pipeline/list",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const preferredPipelineId = pipelineResult?.preferred_pipeline;
|
||||||
|
const pipelines = Array.isArray(pipelineResult?.pipelines)
|
||||||
|
? pipelineResult.pipelines
|
||||||
|
: [];
|
||||||
|
const preferredPipeline =
|
||||||
|
pipelines.find((pipeline) => pipeline?.id === preferredPipelineId) || pipelines[0];
|
||||||
|
|
||||||
|
const sttLanguage = preferredPipeline?.stt_language || preferredPipeline?.language;
|
||||||
|
const ttsLanguage =
|
||||||
|
preferredPipeline?.tts_language || preferredPipeline?.language || preferredPipeline?.stt_language;
|
||||||
|
|
||||||
|
if (sttLanguage) {
|
||||||
|
this._integrationVoiceLanguage = this._normalizeSpeechLanguage(sttLanguage);
|
||||||
|
}
|
||||||
|
if (ttsLanguage) {
|
||||||
|
this._integrationTtsLanguage = this._normalizeSpeechLanguage(ttsLanguage);
|
||||||
|
}
|
||||||
|
} catch (pipelineErr) {
|
||||||
|
console.debug("OpenClaw: assist pipeline language detection skipped:", pipelineErr);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._alwaysVoiceMode && !this._isVoiceMode) {
|
if (this._alwaysVoiceMode && !this._isVoiceMode) {
|
||||||
this._isVoiceMode = true;
|
this._isVoiceMode = true;
|
||||||
this._startVoiceRecognition();
|
this._startVoiceRecognition();
|
||||||
@@ -479,6 +511,20 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
return this._normalizeSpeechLanguage(preferred);
|
return this._normalizeSpeechLanguage(preferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getSpeechSynthesisLanguage() {
|
||||||
|
const configuredLang = this._config.voice_language;
|
||||||
|
const preferred =
|
||||||
|
configuredLang ||
|
||||||
|
this._integrationTtsLanguage ||
|
||||||
|
this._integrationVoiceLanguage ||
|
||||||
|
this._hass?.locale?.language ||
|
||||||
|
this._hass?.selectedLanguage ||
|
||||||
|
this._hass?.language ||
|
||||||
|
navigator.language ||
|
||||||
|
"en-US";
|
||||||
|
return this._normalizeSpeechLanguage(preferred);
|
||||||
|
}
|
||||||
|
|
||||||
_isLikelyBraveBrowser() {
|
_isLikelyBraveBrowser() {
|
||||||
const ua = (navigator.userAgent || "").toLowerCase();
|
const ua = (navigator.userAgent || "").toLowerCase();
|
||||||
const uaDataBrands = navigator.userAgentData?.brands || [];
|
const uaDataBrands = navigator.userAgentData?.brands || [];
|
||||||
@@ -685,7 +731,7 @@ class OpenClawChatCard extends HTMLElement {
|
|||||||
if (!("speechSynthesis" in window)) return;
|
if (!("speechSynthesis" in window)) return;
|
||||||
// Strip markdown for TTS
|
// Strip markdown for TTS
|
||||||
const plain = text.replace(/[*_`#\[\]()]/g, "");
|
const plain = text.replace(/[*_`#\[\]()]/g, "");
|
||||||
const language = this._getSpeechRecognitionLanguage();
|
const language = this._getSpeechSynthesisLanguage();
|
||||||
|
|
||||||
const speakNow = () => {
|
const speakNow = () => {
|
||||||
const utterance = new SpeechSynthesisUtterance(plain);
|
const utterance = new SpeechSynthesisUtterance(plain);
|
||||||
|
|||||||
@@ -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.28";
|
const src = "/openclaw/openclaw-chat-card.js?v=0.1.29";
|
||||||
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