From 28df66c5d869f3f1e45ebd9736ab09b22d227884 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Fri, 17 Jul 2026 20:33:19 +0400 Subject: [PATCH] Added the option 'muted' and hass.callService per stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm trying to implement Hikvision doorbell using only WebRTC Camera. The idea was https://github.com/pergolafabio/Hikvision-Addons/blob/main/advanced_card/twowayaudio.with.advanced.camera.card.md But go2rtc is native for WebRTC Camera. The minimal card configuration is the following: ``` - type: 'custom:webrtc-camera' poster: mycam_snapshot ui: true muted: false streams: - url: mycam_2way_audio mode: webrtc name: "😶" muted: true media: video,audio service: button.press service_data: entity_id: button.ds_kh9510_answer_call - url: mycam_2way_audio mode: webrtc name: "🗣️" muted: false media: video,audio,microphone service: text.set_value service_data: entity_id: text.ds_kd8003_isapi_request value: PUT /ISAPI/System/TwoWayAudio/channels/1/close message: "Domofon Call" shortcuts: - name: Unlock icon: mdi:door-open service: switch.turn_on service_data: entity_id: switch.ds_kd8003_door_relay_0 style: "video {aspect-ratio: 16/9; object-fit: fill;} .pictureinpicture {display: none} .mode {display: none} .screenshot {display: none} .stream {font-size: 60px} ha-icon { --mdc-icon-size: 100px; }" ``` If button.press for button.ds_kh9510_answer_call is not enough you can create a script and call it here instead of button.press. --- custom_components/webrtc/www/webrtc-camera.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/custom_components/webrtc/www/webrtc-camera.js b/custom_components/webrtc/www/webrtc-camera.js index f65b239d..da08b018 100644 --- a/custom_components/webrtc/www/webrtc-camera.js +++ b/custom_components/webrtc/www/webrtc-camera.js @@ -69,7 +69,7 @@ class WebRTCCamera extends VideoRTC { }, config); this.streamID = -1; - this.nextStream(false); + this.nextStream(false, false); this.onhass = []; } @@ -109,7 +109,7 @@ class WebRTCCamera extends VideoRTC { } /** @param reload {boolean} */ - nextStream(reload) { + nextStream(reload, click) { this.streamID = (this.streamID + 1) % this.config.streams.length; const stream = this.config.streams[this.streamID]; @@ -117,11 +117,20 @@ class WebRTCCamera extends VideoRTC { this.config.entity = stream.entity; this.mode = stream.mode || this.config.mode; this.media = stream.media || this.config.media; + this.muted = stream.muted || this.config.muted; + if (this.video) this.video.muted = this.muted; if (reload) { this.ondisconnect(); setTimeout(() => this.onconnect(), 100); // wait ws.close event } + + if (click) { + if (stream.service !== undefined) { + const [domain, name] = stream.service.split('.'); + this.hass.callService(domain, name, stream.service_data || {}); + } + } } /** @return {string} */ @@ -265,9 +274,9 @@ class WebRTCCamera extends VideoRTC { this.querySelector('.ptz-transform').appendChild(this.video); const mode = this.querySelector('.mode'); - mode.addEventListener('click', () => this.nextStream(true)); + mode.addEventListener('click', () => this.nextStream(true, false)); - if (this.config.muted) this.video.muted = true; + this.video.muted = this.muted; if (this.config.poster_remote) this.video.poster = this.config.poster; } @@ -562,8 +571,9 @@ class WebRTCCamera extends VideoRTC { } else if (icon === 'mdi:rectangle') { document.exitPictureInPicture().catch(console.warn); } else if (ev.target.className === 'stream') { - this.nextStream(true); + this.nextStream(true, true); ev.target.innerText = this.streamName; + this.querySelector('.volume').icon = this.muted ? 'mdi:volume-mute' : 'mdi:volume-high'; } });