Skip to content

Feature: Using websockets for STT streaming - #31

Merged
EuleMitKeule merged 13 commits into
EuleMitKeule:masterfrom
tobiloeb:feature/websockets_for_stt
May 2, 2026
Merged

EuleMitKeule merged 13 commits into
EuleMitKeule:masterfrom
tobiloeb:feature/websockets_for_stt

Conversation

@tobiloeb

Copy link
Copy Markdown
Contributor

Hi,

I really like your project and I am currently using it with n8n.
Currently, the integration doesn't stream the STT audio (because n8n can't handle it well), but other systems could. ;) And there are some custom n8n-nodes which provide websocket triggers. So, I extended your integration with the option to configure a ws:// STT endpoint. If a WebSocket is configured, the audio will be streamed. Nothing changes for existing users using http or https.

What do you think?
WebSockets doesn't provide a Basic Authentication header. I'm not sure about the default behavior for WebSocket authentication in HA — I've read about several options. My approach is to send the base64-encoded basic auth string with the other metadata information in the first WebSocket message.

I have tested my changes with the current HA Version 2026.2.2 and a small python Websocket server. Its working well and STT is now very fast. :)

If you have any questions or suggestions, let me know. I just thought that some others can benefit from streaming STT.

Regards,
Tobias

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds optional WebSocket-based streaming for the STT subentry so audio can be sent incrementally (instead of base64-over-HTTP), while keeping existing HTTP/HTTPS behavior unchanged.

Changes:

  • Add a WebSocket code path in the STT entity to stream audio chunks and receive a JSON transcription response.
  • Refactor auth helpers to expose the base64 basic-auth credential string for reuse beyond HTTP headers.
  • Extend config validation and documentation to describe/allow WebSocket STT usage.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
custom_components/webhook_conversation/stt.py Implements WebSocket streaming branch for STT and factors response parsing into a helper.
custom_components/webhook_conversation/models.py Adds a TypedDict for the initial STT WebSocket metadata payload.
custom_components/webhook_conversation/entity.py Adds _get_basic_auth() helper and reuses it in _get_auth_headers().
custom_components/webhook_conversation/config_flow.py Updates webhook URL validation (currently allowing ws://) and minor schema formatting changes.
README.md Documents STT-over-WebSocket payload shape and expected behavior.
Comments suppressed due to low confidence (1)

custom_components/webhook_conversation/config_flow.py:305

  • This URL validation allows ws:// for all subentry types, but only STT implements a WebSocket client. Configuring a conversation/ai_task/tts subentry with ws:// will later fail when the code tries to POST to it. Consider allowing ws:///wss:// only when self._subentry_type == "stt" (and keep other types limited to http/https).
        webhook_url: str = user_input[CONF_WEBHOOK_URL]
        if (
            not webhook_url.startswith("http://")
            and not webhook_url.startswith("https://")
            and not webhook_url.startswith("ws://")
        ):
            _LOGGER.error("Invalid webhook URL: %s", webhook_url)
            errors["base"] = "invalid_webhook_url"

        if self._subentry_type in ("tts", "stt"):

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread README.md
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py Outdated
Comment thread custom_components/webhook_conversation/stt.py
@EuleMitKeule

Copy link
Copy Markdown
Owner

@tobiloeb Thanks for the contribution! Please consider the failing mypy check and the comments made by copilot.

@EuleMitKeule
EuleMitKeule marked this pull request as draft March 2, 2026 20:05
@tobiloeb

tobiloeb commented Mar 4, 2026

Copy link
Copy Markdown
Contributor Author

Good catches from Copilot. I refactored the websockets to aiohttp websockets. I like that because its standard in HA. I have tested the changes and everything is working (with and without basic auth).
I can't test the "wss" endpoint easily but I think this should work out of the box, because aiohttp handle it and its just the secure protocol. 🙂

@EuleMitKeule

Copy link
Copy Markdown
Owner

@tobiloeb Sorry I kinda forgot about this. Any reason for closing the PR other than not getting an answer/getting merged, or can I merge it?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +297 to 302
if (
not webhook_url.startswith("http://")
and not webhook_url.startswith("https://")
and not webhook_url.startswith("ws://")
and not webhook_url.startswith("wss://")
):
Comment on lines +182 to +184
async for chunk in stream:
await ws.send_bytes(chunk)

headers = {"Content-Type": "application/json"}

username, password = self._get_basic_auth()
if username and password is not None:

try:
username, password = self._get_basic_auth()
if username and password is not None:
Comment thread README.md Outdated
@EuleMitKeule EuleMitKeule reopened this May 2, 2026
EuleMitKeule and others added 2 commits May 2, 2026 23:22
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@EuleMitKeule

EuleMitKeule commented May 2, 2026 •

Copy link
Copy Markdown
Owner

@copilot fix the remaining review comments, check mypy and ruff

Copilot AI added a commit that referenced this pull request May 2, 2026
@EuleMitKeule
EuleMitKeule merged commit 3432689 into EuleMitKeule:master May 2, 2026
3 of 5 checks passed
@EuleMitKeule

Copy link
Copy Markdown
Owner

🎉 This PR is included in version 1.12.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants