Conversation
…ive views Every live view made Ring route the intercom's incoming audio to the WebRTC session (live_view audio_enabled=true + camera_options stealth_mode=false, which ring-client-api calls activateCameraSpeaker), so the physical handset kept video but lost its speaker while a stream was open. Fine for the companion card, wrong for consumers that never play audio or don't need it: go2rtc/Frigate restreams, or a TV display that shows the visitor while the user talks on the physical handset. Add switch.<device>_audio (default on = current behaviour, state restored). Off makes live views video-only: live_view goes out with audio_enabled=false and camera_options with stealth_mode=true. Toggling while sessions run is applied in place via stream_options / camera_options session messages, no renegotiation. All sessions now go through an AudioGate websocket proxy on a RingWebRtcStream subclass; no library code is copied. Offers flagged video-only (session attribute "a=x-video-only", or all audio m-lines inactive / rejected) never take the audio regardless of the switch, which is how a go2rtc/WHEP bridge that cannot alter its own offer asks for it. Rewriting the audio m-line itself made Ring stop sending video, hence the out-of-band attribute. Tested on a Ring Intercom Handset Video (BTicino handset) with go2rtc (WHEP) as consumer: video streams, the handset keeps hearing the visitor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author
|
Mention that the initial feature in mind was to allow asking for a video-only session (for go2rtc to consume and display on a TV monitor without blocking the physical handset audio). The switch was added later to add value to the HA camera entity, taking advantage of the PR. |
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.
Audio switch: keep the physical handset's speaker during live views
Problem
Every live view makes Ring route the intercom's incoming audio to the WebRTC session: the physical handset keeps showing video but its speaker goes silent (you can talk, you can't hear) until the stream closes. That's the right behaviour for the camera entity/companion card, which plays that audio, but wrong when the picture is consumed by something that never plays audio or does not need it — a go2rtc/Frigate restream for recording, or a TV/kiosk display that shows who is at the door while the user still picks up the physical handset to talk. In that second case the current behaviour is actively harmful: the handset rings, the TV shows the visitor, the user picks up and can't hear them.
Cause
Two things in the signaling
python-ring-doorbellsends for every session:live_view→stream_options: {audio_enabled: true}camera_connected→camera_options: {stealth_mode: false}— this is whatring-client-apicallsactivateCameraSpeaker().The snapshot path in this component already uses
audio_enabled: falseand doesn't sendcamera_options, and snapshots don't take the handset audio.Change
switch.<device>_audio, default on (= today's behaviour), state restored across restarts._GatedRingWebRtcStream, aRingWebRtcStreamsubclass whosewebsocketproperty wraps the signaling socket in anAudioGate. The gate rewrites exactly two outgoing messages —live_view→stream_options.audio_enabled,camera_options→stealth_mode = not audio— and delegates everything else (recv,close,async for). No library code is copied.stream_options+camera_optionsare sent as session messages (the same wayring-client-apisends them afteractivate_session). No renegotiation, no gap in the picture. The browser's audio transceiver isrecvonlyfrom the start and simply plays whatever RTP arrives.a=x-video-onlyattribute, or when every audio m-line isa=inactive/ rejected (port 0). That is how a go2rtc/WHEP bridge, which cannot alter its own offer, asks for a session that never takes the audio.New files:
audio.py(policy, gate, hot toggle),switch.py.__init__.pyloses the conditional class and gains the gate.Why the out-of-band attribute
Rewriting the audio m-line of the offer itself (
a=inactiveor port 0) makes Ring stop sending video, so a consumer that can't change its own offer (go2rtc's WHEP client always offers video+audio recvonly) needs a way to flag the session that doesn't alter the media description. Unknown session attributes are ignored by Ring.Tested
Ring Intercom Handset Video on a BTicino analog handset, HA 2026.x, go2rtc (Frigate-embedded) consuming through a small WHEP→
camera/webrtc/offerbridge that insertsa=x-video-only:WebRTC <id>: audio=False,live_view sent with stream_options={'audio_enabled': False, ...},camera_options sent as {'stealth_mode': True, ...}Unit-checked against the real
RingWebRtcStreamclass: offer classifier, gate rewrite in both states, passthrough ofrecv/close/async for, and the hot toggle (stream_options+camera_optionscarry the session'ssession_id/dialog_id, later librarycamera_optionsfollow the new state).Hot toggle on hardware: on mid-session works every time (handset mutes, live view gets audio). Off mid-session reliably mutes the live view; the handset usually gets its audio back at once, but occasionally only when the session ends.
apply_audio_livesendsstealth_mode: truefirst and repeats it after 1 s to improve the odds; documented as a known limitation in the README.Docs
README gets an "Audio switch" section under the Lovelace options.
🤖 Generated with Claude Code