Skip to content

Commit e2fbd7a

Browse files
author
techartdev
committed
Update changelog for version 0.1.18, add voice status feedback for recognition states, improve wake word handling, and enhance error reporting for unsupported browsers.
1 parent 9a22cea commit e2fbd7a

4 files changed

Lines changed: 62 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to the OpenClaw Home Assistant Integration will be documented in this file.
44

5+
## [0.1.18] - 2026-02-20
6+
7+
### Fixed
8+
- Voice input now requires wake word only for continuous voice mode, not for manual mic usage.
9+
- Added in-card voice status feedback (listening, wake-word wait, sending, error) to make microphone behavior visible.
10+
- Improved handling for unsupported speech-recognition browsers with explicit UI status.
11+
512
## [0.1.17] - 2026-02-20
613

714
### Fixed

custom_components/openclaw/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"iot_class": "local_polling",
99
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
1010
"requirements": [],
11-
"version": "0.1.17",
11+
"version": "0.1.18",
1212
"dependencies": ["conversation"],
1313
"after_dependencies": ["hassio", "lovelace"]
1414
}

custom_components/openclaw/www/openclaw-chat-card.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class OpenClawChatCard extends HTMLElement {
6363
this._wakeWordEnabled = false;
6464
this._wakeWord = "hey openclaw";
6565
this._alwaysVoiceMode = false;
66+
this._voiceStatus = "";
6667
}
6768

6869
// ── HA card interface ───────────────────────────────────────────────
@@ -391,6 +392,8 @@ class OpenClawChatCard extends HTMLElement {
391392
_startVoiceRecognition() {
392393
if (!("webkitSpeechRecognition" in window) && !("SpeechRecognition" in window)) {
393394
console.warn("OpenClaw: Speech recognition not supported in this browser");
395+
this._voiceStatus = "Speech recognition not supported by this browser.";
396+
this._render();
394397
return;
395398
}
396399

@@ -399,36 +402,50 @@ class OpenClawChatCard extends HTMLElement {
399402
this._recognition.continuous = this._isVoiceMode;
400403
this._recognition.interimResults = true;
401404
this._recognition.lang = this._hass?.language || "en-US";
405+
this._voiceStatus = this._isVoiceMode
406+
? `Listening (wake word: ${this._wakeWord || "hey openclaw"})`
407+
: "Listening…";
402408

403409
this._recognition.onresult = (event) => {
404410
const result = event.results[event.results.length - 1];
405411
if (result.isFinal) {
406412
const text = result[0].transcript?.trim();
407413
if (!text) return;
408414

409-
if (this._wakeWordEnabled) {
415+
const requireWakeWord = this._wakeWordEnabled && this._isVoiceMode;
416+
417+
if (requireWakeWord) {
410418
const wake = this._wakeWord || "hey openclaw";
411419
const lower = text.toLowerCase();
412420
const wakePos = lower.indexOf(wake);
413421
if (wakePos < 0) {
422+
this._voiceStatus = `Heard: \"${text}\" (waiting for wake word)`;
423+
this._render();
414424
return;
415425
}
416426

417427
let command = text.slice(wakePos + wake.length).trim();
418428
command = command.replace(/^[,:;.!?\-]+\s*/, "");
419429
if (!command) {
430+
this._voiceStatus = "Wake word detected. Say command after wake word.";
431+
this._render();
420432
return;
421433
}
434+
this._voiceStatus = "Sending…";
435+
this._render();
422436
this._sendMessage(command);
423437
return;
424438
}
425439

440+
this._voiceStatus = "Sending…";
441+
this._render();
426442
this._sendMessage(text);
427443
}
428444
};
429445

430446
this._recognition.onerror = (event) => {
431447
console.error("OpenClaw: Speech recognition error:", event.error);
448+
this._voiceStatus = `Voice error: ${event.error}`;
432449
this._render();
433450
};
434451

@@ -441,6 +458,7 @@ class OpenClawChatCard extends HTMLElement {
441458
// Ignore — may already be started
442459
}
443460
} else {
461+
this._voiceStatus = "";
444462
this._render();
445463
}
446464
};
@@ -455,6 +473,7 @@ class OpenClawChatCard extends HTMLElement {
455473
this._recognition = null;
456474
}
457475
this._isVoiceMode = false;
476+
this._voiceStatus = "";
458477
}
459478

460479
_toggleVoiceMode() {
@@ -740,6 +759,11 @@ class OpenClawChatCard extends HTMLElement {
740759
animation: pulse 1.5s infinite;
741760
margin-right: 4px;
742761
}
762+
.voice-status {
763+
padding: 4px 16px 0 16px;
764+
font-size: 12px;
765+
color: var(--oc-text-secondary);
766+
}
743767
@keyframes pulse {
744768
0%, 100% { opacity: 1; }
745769
50% { opacity: 0.3; }
@@ -777,6 +801,8 @@ class OpenClawChatCard extends HTMLElement {
777801
}
778802
</div>
779803
804+
${this._voiceStatus ? `<div class="voice-status">${this._escapeHtml(this._voiceStatus)}</div>` : ""}
805+
780806
<div class="input-area">
781807
<textarea
782808
id="input"

www/openclaw-chat-card.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class OpenClawChatCard extends HTMLElement {
6363
this._wakeWordEnabled = false;
6464
this._wakeWord = "hey openclaw";
6565
this._alwaysVoiceMode = false;
66+
this._voiceStatus = "";
6667
}
6768

6869
// ── HA card interface ───────────────────────────────────────────────
@@ -391,6 +392,8 @@ class OpenClawChatCard extends HTMLElement {
391392
_startVoiceRecognition() {
392393
if (!("webkitSpeechRecognition" in window) && !("SpeechRecognition" in window)) {
393394
console.warn("OpenClaw: Speech recognition not supported in this browser");
395+
this._voiceStatus = "Speech recognition not supported by this browser.";
396+
this._render();
394397
return;
395398
}
396399

@@ -399,36 +402,50 @@ class OpenClawChatCard extends HTMLElement {
399402
this._recognition.continuous = this._isVoiceMode;
400403
this._recognition.interimResults = true;
401404
this._recognition.lang = this._hass?.language || "en-US";
405+
this._voiceStatus = this._isVoiceMode
406+
? `Listening (wake word: ${this._wakeWord || "hey openclaw"})`
407+
: "Listening…";
402408

403409
this._recognition.onresult = (event) => {
404410
const result = event.results[event.results.length - 1];
405411
if (result.isFinal) {
406412
const text = result[0].transcript?.trim();
407413
if (!text) return;
408414

409-
if (this._wakeWordEnabled) {
415+
const requireWakeWord = this._wakeWordEnabled && this._isVoiceMode;
416+
417+
if (requireWakeWord) {
410418
const wake = this._wakeWord || "hey openclaw";
411419
const lower = text.toLowerCase();
412420
const wakePos = lower.indexOf(wake);
413421
if (wakePos < 0) {
422+
this._voiceStatus = `Heard: \"${text}\" (waiting for wake word)`;
423+
this._render();
414424
return;
415425
}
416426

417427
let command = text.slice(wakePos + wake.length).trim();
418428
command = command.replace(/^[,:;.!?\-]+\s*/, "");
419429
if (!command) {
430+
this._voiceStatus = "Wake word detected. Say command after wake word.";
431+
this._render();
420432
return;
421433
}
434+
this._voiceStatus = "Sending…";
435+
this._render();
422436
this._sendMessage(command);
423437
return;
424438
}
425439

440+
this._voiceStatus = "Sending…";
441+
this._render();
426442
this._sendMessage(text);
427443
}
428444
};
429445

430446
this._recognition.onerror = (event) => {
431447
console.error("OpenClaw: Speech recognition error:", event.error);
448+
this._voiceStatus = `Voice error: ${event.error}`;
432449
this._render();
433450
};
434451

@@ -441,6 +458,7 @@ class OpenClawChatCard extends HTMLElement {
441458
// Ignore — may already be started
442459
}
443460
} else {
461+
this._voiceStatus = "";
444462
this._render();
445463
}
446464
};
@@ -455,6 +473,7 @@ class OpenClawChatCard extends HTMLElement {
455473
this._recognition = null;
456474
}
457475
this._isVoiceMode = false;
476+
this._voiceStatus = "";
458477
}
459478

460479
_toggleVoiceMode() {
@@ -740,6 +759,11 @@ class OpenClawChatCard extends HTMLElement {
740759
animation: pulse 1.5s infinite;
741760
margin-right: 4px;
742761
}
762+
.voice-status {
763+
padding: 4px 16px 0 16px;
764+
font-size: 12px;
765+
color: var(--oc-text-secondary);
766+
}
743767
@keyframes pulse {
744768
0%, 100% { opacity: 1; }
745769
50% { opacity: 0.3; }
@@ -777,6 +801,8 @@ class OpenClawChatCard extends HTMLElement {
777801
}
778802
</div>
779803
804+
${this._voiceStatus ? `<div class="voice-status">${this._escapeHtml(this._voiceStatus)}</div>` : ""}
805+
780806
<div class="input-area">
781807
<textarea
782808
id="input"

0 commit comments

Comments
 (0)