Remove all ice_servers on native sync WebRTC cameras (#129819)

This commit is contained in:
Robert Resch 2024-11-04 18:41:37 +01:00 committed by GitHub
parent f6e36615d6
commit df796d432e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 68 deletions

View File

@ -827,16 +827,17 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the WebRTC client configuration and extend it with the registered ice servers."""
config = self._async_get_webrtc_client_configuration()
ice_servers = [
server
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
for server in servers()
]
config.configuration.ice_servers.extend(ice_servers)
if not self._webrtc_sync_offer:
# Until 2024.11, the frontend was not resolving any ice servers
# The async approach was added 2024.11 and new integrations need to use it
ice_servers = [
server
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
for server in servers()
]
config.configuration.ice_servers.extend(ice_servers)
config.get_candidates_upfront = (
self._webrtc_sync_offer or self._legacy_webrtc_provider is not None
)
config.get_candidates_upfront = self._legacy_webrtc_provider is not None
return config

View File

@ -1,13 +1,14 @@
"""Test helpers for camera."""
from collections.abc import AsyncGenerator, Generator
from unittest.mock import AsyncMock, PropertyMock, patch
from unittest.mock import AsyncMock, Mock, PropertyMock, patch
import pytest
from homeassistant.components import camera
from homeassistant.components.camera.const import StreamType
from homeassistant.components.camera.webrtc import WebRTCAnswer, WebRTCSendMessage
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
@ -15,6 +16,15 @@ from homeassistant.setup import async_setup_component
from .common import STREAM_SOURCE, WEBRTC_ANSWER
from tests.common import (
MockConfigEntry,
MockModule,
mock_config_flow,
mock_integration,
mock_platform,
setup_test_component_platform,
)
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant) -> None:
@ -142,3 +152,66 @@ def mock_stream_source_fixture() -> Generator[AsyncMock]:
return_value=STREAM_SOURCE,
) as mock_stream_source:
yield mock_stream_source
@pytest.fixture
async def mock_camera_webrtc_native_sync_offer(hass: HomeAssistant) -> None:
"""Initialize a test camera with native sync WebRTC support."""
# Cannot use the fixture mock_camera_web_rtc as it's mocking Camera.async_handle_web_rtc_offer
# and native support is checked by verify the function "async_handle_web_rtc_offer" was
# overwritten(implemented) or not
class MockCamera(camera.Camera):
"""Mock Camera Entity."""
_attr_name = "Test"
_attr_supported_features: camera.CameraEntityFeature = (
camera.CameraEntityFeature.STREAM
)
_attr_frontend_stream_type: camera.StreamType = camera.StreamType.WEB_RTC
async def stream_source(self) -> str | None:
return STREAM_SOURCE
async def async_handle_web_rtc_offer(self, offer_sdp: str) -> str | None:
return WEBRTC_ANSWER
domain = "test"
entry = MockConfigEntry(domain=domain)
entry.add_to_hass(hass)
async def async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(
config_entry, [camera.DOMAIN]
)
return True
async def async_unload_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Unload test config entry."""
await hass.config_entries.async_forward_entry_unload(
config_entry, camera.DOMAIN
)
return True
mock_integration(
hass,
MockModule(
domain,
async_setup_entry=async_setup_entry_init,
async_unload_entry=async_unload_entry_init,
),
)
setup_test_component_platform(
hass, camera.DOMAIN, [MockCamera()], from_config_entry=True
)
mock_platform(hass, f"{domain}.config_flow", Mock())
with mock_config_flow(domain, ConfigFlow):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

View File

@ -25,7 +25,6 @@ from homeassistant.components.camera.const import (
)
from homeassistant.components.camera.helper import get_camera_from_entity_id
from homeassistant.components.websocket_api import TYPE_RESULT
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import (
ATTR_ENTITY_ID,
EVENT_HOMEASSISTANT_STARTED,
@ -38,18 +37,12 @@ from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from .common import EMPTY_8_6_JPEG, STREAM_SOURCE, WEBRTC_ANSWER, mock_turbo_jpeg
from .common import EMPTY_8_6_JPEG, STREAM_SOURCE, mock_turbo_jpeg
from tests.common import (
MockConfigEntry,
MockModule,
async_fire_time_changed,
help_test_all,
import_and_test_deprecated_constant_enum,
mock_config_flow,
mock_integration,
mock_platform,
setup_test_component_platform,
)
from tests.typing import ClientSessionGenerator, WebSocketGenerator
@ -986,62 +979,13 @@ async def test_camera_capabilities_hls(
)
@pytest.mark.usefixtures("mock_camera_webrtc_native_sync_offer")
async def test_camera_capabilities_webrtc(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test WebRTC camera capabilities."""
# Cannot use the fixture mock_camera_web_rtc as it's mocking Camera.async_handle_web_rtc_offer
# Camera capabilities are determined by by checking if the function was overwritten(implemented) or not
class MockCamera(camera.Camera):
"""Mock Camera Entity."""
_attr_name = "Test"
_attr_supported_features: camera.CameraEntityFeature = (
camera.CameraEntityFeature.STREAM
)
async def stream_source(self) -> str | None:
return STREAM_SOURCE
async def async_handle_web_rtc_offer(self, offer_sdp: str) -> str | None:
return WEBRTC_ANSWER
domain = "test"
entry = MockConfigEntry(domain=domain)
entry.add_to_hass(hass)
async def async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
return True
async def async_unload_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Unload test config entry."""
await hass.config_entries.async_forward_entry_unload(config_entry, DOMAIN)
return True
mock_integration(
hass,
MockModule(
domain,
async_setup_entry=async_setup_entry_init,
async_unload_entry=async_unload_entry_init,
),
)
setup_test_component_platform(hass, DOMAIN, [MockCamera()], from_config_entry=True)
mock_platform(hass, f"{domain}.config_flow", Mock())
with mock_config_flow(domain, ConfigFlow):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await _test_capabilities(
hass, hass_ws_client, "camera.test", {StreamType.WEB_RTC}, {StreamType.WEB_RTC}
)

View File

@ -393,6 +393,29 @@ async def test_ws_get_client_config(
}
@pytest.mark.usefixtures("mock_camera_webrtc_native_sync_offer")
async def test_ws_get_client_config_sync_offer(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test get WebRTC client config, when camera is supporting sync offer."""
await async_setup_component(hass, "camera", {})
await hass.async_block_till_done()
client = await hass_ws_client(hass)
await client.send_json_auto_id(
{"type": "camera/webrtc/get_client_config", "entity_id": "camera.test"}
)
msg = await client.receive_json()
# Assert WebSocket response
assert msg["type"] == TYPE_RESULT
assert msg["success"]
assert msg["result"] == {
"configuration": {},
"getCandidatesUpfront": False,
}
@pytest.mark.usefixtures("mock_camera_webrtc")
async def test_ws_get_client_config_custom_config(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator