Add native WebRTC session foundation (core engine + HA signaling)#7137
Draft
TimoPtr wants to merge 6 commits into
Draft
Add native WebRTC session foundation (core engine + HA signaling)#7137TimoPtr wants to merge 6 commits into
TimoPtr wants to merge 6 commits into
Conversation
First slice of the native WebRTC camera streaming plan (see docs/adr/0001-native-webrtc-session-library.md): - :webrtc-core: UI-free session engine. CameraPlayer/TwoWayAudio interfaces, transport-agnostic SignalingClient, WebRtcSession state machine (trickle ICE both directions, candidate buffering, reconnection with capped backoff, ordered idempotent teardown) over a thin PeerConnectionController facade, plus the libwebrtc-backed implementation using io.github.webrtc-sdk:android-prefixed. - :webrtc-signaling-ha: SignalingClient implementation over the existing WebSocket layer. - :common: camera/capabilities and camera/webrtc/* WebSocket commands (get_client_config, offer subscription, candidate) with a dedicated JSON mapper because these payloads mix snake_case and the camelCase W3C dictionary keys. - Unit tests for the session state machine, signaling client and entity serialization. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
The branch was authored in an environment without egress to Google Maven, so the lockfiles for the new modules could not be generated locally. This mirrors renovate-lockfiles.yml scoped to this branch and will be removed once the lockfiles are committed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
The lockfiles for the new modules are now committed, this branch-scoped workflow is no longer needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
- Remove the literal camera/webrtc/* from KDoc comments: Kotlin block comments nest, so the /* inside opened a comment that never closed - Replace the JUnit Kotlin assertThrows (built for JVM target 17, this project targets 11) with runCatching assertions - ktlintFormat pass over the new files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces the first “foundation” slice of a native WebRTC streaming stack for the Home Assistant Android app: a UI-free, transport-agnostic WebRTC session engine (:webrtc-core) and a Home Assistant WebSocket signaling implementation (:webrtc-signaling-ha), plus the necessary :common WebSocket commands/entities and an ADR capturing the architecture decision.
Changes:
- Added a new
:webrtc-coremodule with a pure-KotlinWebRtcSessionstate machine and a libwebrtc-backedPeerConnectionController. - Added a new
:webrtc-signaling-hamodule implementingSignalingClienton top ofWebSocketRepository. - Extended
:commonWebSocket layer withcamera/capabilities,camera/webrtc/get_client_config,camera/webrtc/offersubscription, andcamera/webrtc/candidate, using a dedicated JSON mapper for mixed snake_case/camelCase payloads.
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| webrtc-signaling-ha/src/test/kotlin/io/homeassistant/companion/android/webrtc/signaling/HaSignalingClientTest.kt | Unit tests for HA signaling mapping and error handling |
| webrtc-signaling-ha/src/main/kotlin/io/homeassistant/companion/android/webrtc/signaling/HaSignalingClient.kt | HA WebSocket-based SignalingClient implementation |
| webrtc-signaling-ha/lint-baseline.xml | Lint baseline for the new module |
| webrtc-signaling-ha/gradle.lockfile | Dependency lockfile for the new module |
| webrtc-signaling-ha/build.gradle.kts | New module Gradle configuration |
| webrtc-core/src/test/kotlin/io/homeassistant/companion/android/webrtc/core/session/WebRtcSessionTest.kt | Unit tests for session lifecycle/state machine behavior |
| webrtc-core/src/test/kotlin/io/homeassistant/companion/android/webrtc/core/session/Fakes.kt | Fakes for signaling + peer connection controller used in tests |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/TwoWayAudio.kt | Two-way audio capability + mic state model |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/signaling/SignalingClient.kt | Transport-agnostic signaling interface + signaling models |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/session/WebRtcSession.kt | Session state machine (negotiation, ICE buffering, reconnect, teardown) |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/session/PeerConnectionController.kt | Facade API for libwebrtc peer connection integration |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/session/libwebrtc/LibWebRtcPeerConnectionControllerFactory.kt | Shared libwebrtc PeerConnectionFactory holder + controller factory |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/session/libwebrtc/LibWebRtcPeerConnectionController.kt | libwebrtc-backed peer connection controller implementation |
| webrtc-core/src/main/kotlin/io/homeassistant/companion/android/webrtc/core/CameraPlayer.kt | Player abstraction + state/failure models |
| webrtc-core/lint-baseline.xml | Lint baseline for the new module |
| webrtc-core/gradle.lockfile | Dependency lockfile for the new module |
| webrtc-core/build.gradle.kts | New module Gradle configuration (incl. libwebrtc dependency) |
| settings.gradle.kts | Includes the two new Gradle modules |
| gradle/libs.versions.toml | Adds the libwebrtc SDK dependency/version entry |
| docs/adr/0001-native-webrtc-session-library.md | ADR documenting the decision/architecture/phasing |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImplTest.kt | Adds unit tests for new camera/WebRTC WebSocket commands |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/WebRtcEventTest.kt | Adds unit tests for WebRTC event/candidate/config serialization |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt | Extends repository API with camera capabilities + WebRTC methods |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt | Implements camera/WebRTC commands and candidate send serialization |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketCoreImpl.kt | Adds WebRTC subscription event decoding path |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketConstants.kt | Adds WebRTC offer subscription type constant |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/WebRtcEvent.kt | Adds dedicated WebRTC JSON mapper + WebRTC event/candidate entities |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/CameraWebRtcClientConfigResponse.kt | Adds WebRTC client config response + ICE server models/serializer |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/CameraCapabilitiesResponse.kt | Adds camera capabilities response + known stream type constants |
- start() now awaits the previous session job so a cancelled session always finishes disposing its peer connection before a new one starts - Cancel the previous grace-period timer on repeated DISCONNECTED events so a stale timer cannot force a reconnection after recovery - getCameraWebRtcClientConfig only decodes successful responses Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
TimoPtr
marked this pull request as draft
July 15, 2026 06:14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First slice of the native WebRTC camera streaming plan: a UI-free WebRTC session engine based on libwebrtc, plus the Home Assistant signaling implementation over the existing WebSocket layer. The motivation, alternatives considered (ExoPlayer
DataSource, keeping the WebView) and the target architecture are captured in the new ADR:docs/adr/0001-native-webrtc-session-library.md.Why: the WebView's WebRTC path cannot do two-way audio on plain-HTTP LAN setups (Chromium's secure-context rule for
getUserMedia), and a doorbell "incoming call" experience (CallStyle notification, pre-negotiated media from a push) cannot be built on a WebView at all. A native session engine fixes both and gives deterministic microphone release (the #6153 class of bugs becomes structural).What's included:
:webrtc-core(new module, the only one with a libwebrtc dependency —io.github.webrtc-sdk:android-prefixed, shaded tolivekit.org.webrtc):CameraPlayer/TwoWayAudiointerfaces (the seam for a later WebRTC → HLS fallback)SignalingClientinterface and modelsWebRtcSession: pure-Kotlin state machine handling negotiation, trickle ICE in both directions, candidate buffering (candidate-before-answer, local-candidate-before-session-id), reconnection with capped exponential backoff (the HA signaling API has no renegotiation, so recovery is a fresh negotiation, not an ICE restart), and ordered, idempotent teardown on every exit pathPeerConnectionControllerfacade + libwebrtc-backed implementation (recvonly video, sendrecv audio with the microphone track created disabled so push-to-talk needs no renegotiation):webrtc-signaling-ha(new module):SignalingClientimplementation overWebSocketRepository:common:camera/capabilitiesandcamera/webrtc/*commands (get_client_config,offersubscription,candidate) with a dedicated JSON mapper, because these payloads mix snake_case keys with the camelCase W3C dictionary keys (sdpMid,iceServers) that the shared snake_case mapper would mangleThere is no consumer wiring yet — dashboard/external-bus integration, the Compose renderer, two-way audio UI and the call flow are follow-up PRs per the ADR's phasing.
Checklist
Any other notes
:common656,:webrtc-core18,:webrtc-signaling-ha8),ktlintCheck, andlint(no new issues) — plus the complete PR CI matrix.RTCIceCandidateInittrickle ICE); consumers will gate on server version andcamera/capabilitieswhen wiring up in the follow-up PRs.🤖 Generated with Claude Code
https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu