mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Remove deprecated camera async_handle_web_rtc_offer function (#144561)
This commit is contained in:
parent
4dde314338
commit
7b23f21712
@ -55,7 +55,6 @@ from homeassistant.helpers.deprecation import (
|
|||||||
DeprecatedConstantEnum,
|
DeprecatedConstantEnum,
|
||||||
all_with_deprecated_constants,
|
all_with_deprecated_constants,
|
||||||
check_if_deprecated_constant,
|
check_if_deprecated_constant,
|
||||||
deprecated_function,
|
|
||||||
dir_with_deprecated_constants,
|
dir_with_deprecated_constants,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
@ -86,10 +85,10 @@ from .prefs import CameraPreferences, DynamicStreamSettings # noqa: F401
|
|||||||
from .webrtc import (
|
from .webrtc import (
|
||||||
DATA_ICE_SERVERS,
|
DATA_ICE_SERVERS,
|
||||||
CameraWebRTCProvider,
|
CameraWebRTCProvider,
|
||||||
WebRTCAnswer,
|
WebRTCAnswer, # noqa: F401
|
||||||
WebRTCCandidate, # noqa: F401
|
WebRTCCandidate, # noqa: F401
|
||||||
WebRTCClientConfiguration,
|
WebRTCClientConfiguration,
|
||||||
WebRTCError,
|
WebRTCError, # noqa: F401
|
||||||
WebRTCMessage, # noqa: F401
|
WebRTCMessage, # noqa: F401
|
||||||
WebRTCSendMessage,
|
WebRTCSendMessage,
|
||||||
async_get_supported_provider,
|
async_get_supported_provider,
|
||||||
@ -473,9 +472,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
self.async_update_token()
|
self.async_update_token()
|
||||||
self._create_stream_lock: asyncio.Lock | None = None
|
self._create_stream_lock: asyncio.Lock | None = None
|
||||||
self._webrtc_provider: CameraWebRTCProvider | None = None
|
self._webrtc_provider: CameraWebRTCProvider | None = None
|
||||||
self._supports_native_sync_webrtc = (
|
|
||||||
type(self).async_handle_web_rtc_offer != Camera.async_handle_web_rtc_offer
|
|
||||||
)
|
|
||||||
self._supports_native_async_webrtc = (
|
self._supports_native_async_webrtc = (
|
||||||
type(self).async_handle_async_webrtc_offer
|
type(self).async_handle_async_webrtc_offer
|
||||||
!= Camera.async_handle_async_webrtc_offer
|
!= Camera.async_handle_async_webrtc_offer
|
||||||
@ -579,15 +575,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def async_handle_web_rtc_offer(self, offer_sdp: str) -> str | None:
|
|
||||||
"""Handle the WebRTC offer and return an answer.
|
|
||||||
|
|
||||||
This is used by cameras with CameraEntityFeature.STREAM
|
|
||||||
and StreamType.WEB_RTC.
|
|
||||||
|
|
||||||
Integrations can override with a native WebRTC implementation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
async def async_handle_async_webrtc_offer(
|
async def async_handle_async_webrtc_offer(
|
||||||
self, offer_sdp: str, session_id: str, send_message: WebRTCSendMessage
|
self, offer_sdp: str, session_id: str, send_message: WebRTCSendMessage
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -600,42 +587,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
|
|
||||||
Integrations can override with a native WebRTC implementation.
|
Integrations can override with a native WebRTC implementation.
|
||||||
"""
|
"""
|
||||||
if self._supports_native_sync_webrtc:
|
|
||||||
try:
|
|
||||||
answer = await deprecated_function(
|
|
||||||
"async_handle_async_webrtc_offer",
|
|
||||||
breaks_in_ha_version="2025.6",
|
|
||||||
)(self.async_handle_web_rtc_offer)(offer_sdp)
|
|
||||||
except ValueError as ex:
|
|
||||||
_LOGGER.error("Error handling WebRTC offer: %s", ex)
|
|
||||||
send_message(
|
|
||||||
WebRTCError(
|
|
||||||
"webrtc_offer_failed",
|
|
||||||
str(ex),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except TimeoutError:
|
|
||||||
# This catch was already here and should stay through the deprecation
|
|
||||||
_LOGGER.error("Timeout handling WebRTC offer")
|
|
||||||
send_message(
|
|
||||||
WebRTCError(
|
|
||||||
"webrtc_offer_failed",
|
|
||||||
"Timeout handling WebRTC offer",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if answer:
|
|
||||||
send_message(WebRTCAnswer(answer))
|
|
||||||
else:
|
|
||||||
_LOGGER.error("Error handling WebRTC offer: No answer")
|
|
||||||
send_message(
|
|
||||||
WebRTCError(
|
|
||||||
"webrtc_offer_failed",
|
|
||||||
"No answer on WebRTC offer",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._webrtc_provider:
|
if self._webrtc_provider:
|
||||||
await self._webrtc_provider.async_handle_async_webrtc_offer(
|
await self._webrtc_provider.async_handle_async_webrtc_offer(
|
||||||
self, offer_sdp, session_id, send_message
|
self, offer_sdp, session_id, send_message
|
||||||
@ -764,9 +715,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
new_provider = None
|
new_provider = None
|
||||||
|
|
||||||
# Skip all providers if the camera has a native WebRTC implementation
|
# Skip all providers if the camera has a native WebRTC implementation
|
||||||
if not (
|
if not self._supports_native_async_webrtc:
|
||||||
self._supports_native_sync_webrtc or self._supports_native_async_webrtc
|
|
||||||
):
|
|
||||||
# Camera doesn't have a native WebRTC implementation
|
# Camera doesn't have a native WebRTC implementation
|
||||||
new_provider = await self._async_get_supported_webrtc_provider(
|
new_provider = await self._async_get_supported_webrtc_provider(
|
||||||
async_get_supported_provider
|
async_get_supported_provider
|
||||||
@ -798,17 +747,12 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
"""Return the WebRTC client configuration and extend it with the registered ice servers."""
|
"""Return the WebRTC client configuration and extend it with the registered ice servers."""
|
||||||
config = self._async_get_webrtc_client_configuration()
|
config = self._async_get_webrtc_client_configuration()
|
||||||
|
|
||||||
if not self._supports_native_sync_webrtc:
|
ice_servers = [
|
||||||
# Until 2024.11, the frontend was not resolving any ice servers
|
server
|
||||||
# The async approach was added 2024.11 and new integrations need to use it
|
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
|
||||||
ice_servers = [
|
for server in servers()
|
||||||
server
|
]
|
||||||
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
|
config.configuration.ice_servers.extend(ice_servers)
|
||||||
for server in servers()
|
|
||||||
]
|
|
||||||
config.configuration.ice_servers.extend(ice_servers)
|
|
||||||
|
|
||||||
config.get_candidates_upfront = self._supports_native_sync_webrtc
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@ -838,7 +782,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
"""Return the camera capabilities."""
|
"""Return the camera capabilities."""
|
||||||
frontend_stream_types = set()
|
frontend_stream_types = set()
|
||||||
if CameraEntityFeature.STREAM in self.supported_features_compat:
|
if CameraEntityFeature.STREAM in self.supported_features_compat:
|
||||||
if self._supports_native_sync_webrtc or self._supports_native_async_webrtc:
|
if self._supports_native_async_webrtc:
|
||||||
# The camera has a native WebRTC implementation
|
# The camera has a native WebRTC implementation
|
||||||
frontend_stream_types.add(StreamType.WEB_RTC)
|
frontend_stream_types.add(StreamType.WEB_RTC)
|
||||||
else:
|
else:
|
||||||
|
@ -111,13 +111,11 @@ class WebRTCClientConfiguration:
|
|||||||
|
|
||||||
configuration: RTCConfiguration = field(default_factory=RTCConfiguration)
|
configuration: RTCConfiguration = field(default_factory=RTCConfiguration)
|
||||||
data_channel: str | None = None
|
data_channel: str | None = None
|
||||||
get_candidates_upfront: bool = False
|
|
||||||
|
|
||||||
def to_frontend_dict(self) -> dict[str, Any]:
|
def to_frontend_dict(self) -> dict[str, Any]:
|
||||||
"""Return a dict that can be used by the frontend."""
|
"""Return a dict that can be used by the frontend."""
|
||||||
data: dict[str, Any] = {
|
data: dict[str, Any] = {
|
||||||
"configuration": self.configuration.to_dict(),
|
"configuration": self.configuration.to_dict(),
|
||||||
"getCandidatesUpfront": self.get_candidates_upfront,
|
|
||||||
}
|
}
|
||||||
if self.data_channel is not None:
|
if self.data_channel is not None:
|
||||||
data["dataChannel"] = self.data_channel
|
data["dataChannel"] = self.data_channel
|
||||||
|
@ -165,13 +165,15 @@ async def mock_test_webrtc_cameras(hass: HomeAssistant) -> None:
|
|||||||
async def stream_source(self) -> str | None:
|
async def stream_source(self) -> str | None:
|
||||||
return STREAM_SOURCE
|
return STREAM_SOURCE
|
||||||
|
|
||||||
class SyncCamera(BaseCamera):
|
class AsyncNoCandidateCamera(BaseCamera):
|
||||||
"""Mock Camera with native sync WebRTC support."""
|
"""Mock Camera with native async WebRTC support but not implemented candidate support."""
|
||||||
|
|
||||||
_attr_name = "Sync"
|
_attr_name = "Async No Candidate"
|
||||||
|
|
||||||
async def async_handle_web_rtc_offer(self, offer_sdp: str) -> str | None:
|
async def async_handle_async_webrtc_offer(
|
||||||
return WEBRTC_ANSWER
|
self, offer_sdp: str, session_id: str, send_message: WebRTCSendMessage
|
||||||
|
) -> None:
|
||||||
|
send_message(WebRTCAnswer(WEBRTC_ANSWER))
|
||||||
|
|
||||||
class AsyncCamera(BaseCamera):
|
class AsyncCamera(BaseCamera):
|
||||||
"""Mock Camera with native async WebRTC support."""
|
"""Mock Camera with native async WebRTC support."""
|
||||||
@ -221,7 +223,10 @@ async def mock_test_webrtc_cameras(hass: HomeAssistant) -> None:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
setup_test_component_platform(
|
setup_test_component_platform(
|
||||||
hass, camera.DOMAIN, [SyncCamera(), AsyncCamera()], from_config_entry=True
|
hass,
|
||||||
|
camera.DOMAIN,
|
||||||
|
[AsyncNoCandidateCamera(), AsyncCamera()],
|
||||||
|
from_config_entry=True,
|
||||||
)
|
)
|
||||||
mock_platform(hass, f"{domain}.config_flow", Mock())
|
mock_platform(hass, f"{domain}.config_flow", Mock())
|
||||||
|
|
||||||
|
@ -968,24 +968,19 @@ async def test_camera_capabilities_webrtc(
|
|||||||
"""Test WebRTC camera capabilities."""
|
"""Test WebRTC camera capabilities."""
|
||||||
|
|
||||||
await _test_capabilities(
|
await _test_capabilities(
|
||||||
hass, hass_ws_client, "camera.sync", {StreamType.WEB_RTC}, {StreamType.WEB_RTC}
|
hass, hass_ws_client, "camera.async", {StreamType.WEB_RTC}, {StreamType.WEB_RTC}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
("entity_id", "expect_native_async_webrtc"),
|
|
||||||
[("camera.sync", False), ("camera.async", True)],
|
|
||||||
)
|
|
||||||
@pytest.mark.usefixtures("mock_test_webrtc_cameras", "register_test_provider")
|
@pytest.mark.usefixtures("mock_test_webrtc_cameras", "register_test_provider")
|
||||||
async def test_webrtc_provider_not_added_for_native_webrtc(
|
async def test_webrtc_provider_not_added_for_native_webrtc(
|
||||||
hass: HomeAssistant, entity_id: str, expect_native_async_webrtc: bool
|
hass: HomeAssistant,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that a WebRTC provider is not added to a camera when the camera has native WebRTC support."""
|
"""Test that a WebRTC provider is not added to a camera when the camera has native WebRTC support."""
|
||||||
camera_obj = get_camera_from_entity_id(hass, entity_id)
|
camera_obj = get_camera_from_entity_id(hass, "camera.async")
|
||||||
assert camera_obj
|
assert camera_obj
|
||||||
assert camera_obj._webrtc_provider is None
|
assert camera_obj._webrtc_provider is None
|
||||||
assert camera_obj._supports_native_sync_webrtc is not expect_native_async_webrtc
|
assert camera_obj._supports_native_async_webrtc is True
|
||||||
assert camera_obj._supports_native_async_webrtc is expect_native_async_webrtc
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
||||||
@ -1016,14 +1011,12 @@ async def test_camera_capabilities_changing_non_native_support(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_test_webrtc_cameras")
|
@pytest.mark.usefixtures("mock_test_webrtc_cameras")
|
||||||
@pytest.mark.parametrize(("entity_id"), ["camera.sync", "camera.async"])
|
|
||||||
async def test_camera_capabilities_changing_native_support(
|
async def test_camera_capabilities_changing_native_support(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
entity_id: str,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test WebRTC camera capabilities."""
|
"""Test WebRTC camera capabilities."""
|
||||||
cam = get_camera_from_entity_id(hass, entity_id)
|
cam = get_camera_from_entity_id(hass, "camera.async")
|
||||||
assert cam.supported_features == camera.CameraEntityFeature.STREAM
|
assert cam.supported_features == camera.CameraEntityFeature.STREAM
|
||||||
|
|
||||||
await _test_capabilities(
|
await _test_capabilities(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Test camera WebRTC."""
|
"""Test camera WebRTC."""
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
import logging
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
@ -10,9 +9,7 @@ from webrtc_models import RTCIceCandidate, RTCIceCandidateInit, RTCIceServer
|
|||||||
|
|
||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import (
|
||||||
DATA_ICE_SERVERS,
|
DATA_ICE_SERVERS,
|
||||||
DOMAIN as CAMERA_DOMAIN,
|
|
||||||
Camera,
|
Camera,
|
||||||
CameraEntityFeature,
|
|
||||||
CameraWebRTCProvider,
|
CameraWebRTCProvider,
|
||||||
StreamType,
|
StreamType,
|
||||||
WebRTCAnswer,
|
WebRTCAnswer,
|
||||||
@ -25,22 +22,12 @@ from homeassistant.components.camera import (
|
|||||||
get_camera_from_entity_id,
|
get_camera_from_entity_id,
|
||||||
)
|
)
|
||||||
from homeassistant.components.websocket_api import TYPE_RESULT
|
from homeassistant.components.websocket_api import TYPE_RESULT
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.core_config import async_process_ha_core_config
|
from homeassistant.core_config import async_process_ha_core_config
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .common import STREAM_SOURCE, WEBRTC_ANSWER, SomeTestProvider
|
from .common import STREAM_SOURCE, WEBRTC_ANSWER, SomeTestProvider
|
||||||
|
|
||||||
from tests.common import (
|
|
||||||
MockConfigEntry,
|
|
||||||
MockModule,
|
|
||||||
mock_config_flow,
|
|
||||||
mock_integration,
|
|
||||||
mock_platform,
|
|
||||||
setup_test_component_platform,
|
|
||||||
)
|
|
||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
WEBRTC_OFFER = "v=0\r\n"
|
WEBRTC_OFFER = "v=0\r\n"
|
||||||
@ -57,84 +44,6 @@ class Go2RTCProvider(SomeTestProvider):
|
|||||||
return "go2rtc"
|
return "go2rtc"
|
||||||
|
|
||||||
|
|
||||||
class MockCamera(Camera):
|
|
||||||
"""Mock Camera Entity."""
|
|
||||||
|
|
||||||
_attr_name = "Test"
|
|
||||||
_attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
"""Initialize the mock entity."""
|
|
||||||
super().__init__()
|
|
||||||
self._sync_answer: str | None | Exception = WEBRTC_ANSWER
|
|
||||||
|
|
||||||
def set_sync_answer(self, value: str | None | Exception) -> None:
|
|
||||||
"""Set sync offer answer."""
|
|
||||||
self._sync_answer = value
|
|
||||||
|
|
||||||
async def async_handle_web_rtc_offer(self, offer_sdp: str) -> str | None:
|
|
||||||
"""Handle the WebRTC offer and return the answer."""
|
|
||||||
if isinstance(self._sync_answer, Exception):
|
|
||||||
raise self._sync_answer
|
|
||||||
return self._sync_answer
|
|
||||||
|
|
||||||
async def stream_source(self) -> str | None:
|
|
||||||
"""Return the source of the stream.
|
|
||||||
|
|
||||||
This is used by cameras with CameraEntityFeature.STREAM
|
|
||||||
and StreamType.HLS.
|
|
||||||
"""
|
|
||||||
return "rtsp://stream"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def init_test_integration(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
) -> MockCamera:
|
|
||||||
"""Initialize components."""
|
|
||||||
|
|
||||||
entry = MockConfigEntry(domain=TEST_INTEGRATION_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(
|
|
||||||
TEST_INTEGRATION_DOMAIN,
|
|
||||||
async_setup_entry=async_setup_entry_init,
|
|
||||||
async_unload_entry=async_unload_entry_init,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
test_camera = MockCamera()
|
|
||||||
setup_test_component_platform(
|
|
||||||
hass, CAMERA_DOMAIN, [test_camera], from_config_entry=True
|
|
||||||
)
|
|
||||||
mock_platform(hass, f"{TEST_INTEGRATION_DOMAIN}.config_flow", Mock())
|
|
||||||
|
|
||||||
with mock_config_flow(TEST_INTEGRATION_DOMAIN, ConfigFlow):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
return test_camera
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
||||||
async def test_async_register_webrtc_provider(
|
async def test_async_register_webrtc_provider(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -302,7 +211,6 @@ async def test_ws_get_client_config(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"getCandidatesUpfront": False,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -341,30 +249,6 @@ async def test_ws_get_client_config(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"getCandidatesUpfront": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_test_webrtc_cameras")
|
|
||||||
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.sync"}
|
|
||||||
)
|
|
||||||
msg = await client.receive_json()
|
|
||||||
|
|
||||||
# Assert WebSocket response
|
|
||||||
assert msg["type"] == TYPE_RESULT
|
|
||||||
assert msg["success"]
|
|
||||||
assert msg["result"] == {
|
|
||||||
"configuration": {},
|
|
||||||
"getCandidatesUpfront": True,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -391,7 +275,6 @@ async def test_ws_get_client_config_custom_config(
|
|||||||
assert msg["success"]
|
assert msg["success"]
|
||||||
assert msg["result"] == {
|
assert msg["result"] == {
|
||||||
"configuration": {"iceServers": [{"urls": ["stun:custom_stun_server:3478"]}]},
|
"configuration": {"iceServers": [{"urls": ["stun:custom_stun_server:3478"]}]},
|
||||||
"getCandidatesUpfront": False,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -625,144 +508,6 @@ async def test_websocket_webrtc_offer_missing_offer(
|
|||||||
assert response["error"]["code"] == "invalid_format"
|
assert response["error"]["code"] == "invalid_format"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
("error", "expected_message"),
|
|
||||||
[
|
|
||||||
(ValueError("value error"), "value error"),
|
|
||||||
(HomeAssistantError("offer failed"), "offer failed"),
|
|
||||||
(TimeoutError(), "Timeout handling WebRTC offer"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_websocket_webrtc_offer_failure(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
init_test_integration: MockCamera,
|
|
||||||
error: Exception,
|
|
||||||
expected_message: str,
|
|
||||||
) -> None:
|
|
||||||
"""Test WebRTC stream that fails handling the offer."""
|
|
||||||
client = await hass_ws_client(hass)
|
|
||||||
init_test_integration.set_sync_answer(error)
|
|
||||||
|
|
||||||
await client.send_json_auto_id(
|
|
||||||
{
|
|
||||||
"type": "camera/webrtc/offer",
|
|
||||||
"entity_id": "camera.test",
|
|
||||||
"offer": WEBRTC_OFFER,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
response = await client.receive_json()
|
|
||||||
|
|
||||||
assert response["type"] == TYPE_RESULT
|
|
||||||
assert response["success"]
|
|
||||||
subscription_id = response["id"]
|
|
||||||
|
|
||||||
# Session id
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"]["type"] == "session"
|
|
||||||
|
|
||||||
# Error
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"] == {
|
|
||||||
"type": "error",
|
|
||||||
"code": "webrtc_offer_failed",
|
|
||||||
"message": expected_message,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_test_webrtc_cameras")
|
|
||||||
async def test_websocket_webrtc_offer_sync(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
caplog: pytest.LogCaptureFixture,
|
|
||||||
) -> None:
|
|
||||||
"""Test sync WebRTC stream offer."""
|
|
||||||
client = await hass_ws_client(hass)
|
|
||||||
|
|
||||||
await client.send_json_auto_id(
|
|
||||||
{
|
|
||||||
"type": "camera/webrtc/offer",
|
|
||||||
"entity_id": "camera.sync",
|
|
||||||
"offer": WEBRTC_OFFER,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
response = await client.receive_json()
|
|
||||||
|
|
||||||
assert (
|
|
||||||
"tests.components.camera.conftest",
|
|
||||||
logging.WARNING,
|
|
||||||
(
|
|
||||||
"async_handle_web_rtc_offer was called from camera, this is a deprecated "
|
|
||||||
"function which will be removed in HA Core 2025.6. Use "
|
|
||||||
"async_handle_async_webrtc_offer instead"
|
|
||||||
),
|
|
||||||
) in caplog.record_tuples
|
|
||||||
assert response["type"] == TYPE_RESULT
|
|
||||||
assert response["success"]
|
|
||||||
subscription_id = response["id"]
|
|
||||||
|
|
||||||
# Session id
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"]["type"] == "session"
|
|
||||||
|
|
||||||
# Answer
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"] == {"type": "answer", "answer": WEBRTC_ANSWER}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_websocket_webrtc_offer_sync_no_answer(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
caplog: pytest.LogCaptureFixture,
|
|
||||||
init_test_integration: MockCamera,
|
|
||||||
) -> None:
|
|
||||||
"""Test sync WebRTC stream offer with no answer."""
|
|
||||||
client = await hass_ws_client(hass)
|
|
||||||
init_test_integration.set_sync_answer(None)
|
|
||||||
|
|
||||||
await client.send_json_auto_id(
|
|
||||||
{
|
|
||||||
"type": "camera/webrtc/offer",
|
|
||||||
"entity_id": "camera.test",
|
|
||||||
"offer": WEBRTC_OFFER,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
response = await client.receive_json()
|
|
||||||
|
|
||||||
assert response["type"] == TYPE_RESULT
|
|
||||||
assert response["success"]
|
|
||||||
subscription_id = response["id"]
|
|
||||||
|
|
||||||
# Session id
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"]["type"] == "session"
|
|
||||||
|
|
||||||
# Answer
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["id"] == subscription_id
|
|
||||||
assert response["type"] == "event"
|
|
||||||
assert response["event"] == {
|
|
||||||
"type": "error",
|
|
||||||
"code": "webrtc_offer_failed",
|
|
||||||
"message": "No answer on WebRTC offer",
|
|
||||||
}
|
|
||||||
assert (
|
|
||||||
"homeassistant.components.camera",
|
|
||||||
logging.ERROR,
|
|
||||||
"Error handling WebRTC offer: No answer",
|
|
||||||
) in caplog.record_tuples
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_camera")
|
@pytest.mark.usefixtures("mock_camera")
|
||||||
async def test_websocket_webrtc_offer_invalid_stream_type(
|
async def test_websocket_webrtc_offer_invalid_stream_type(
|
||||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
|
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
|
||||||
@ -901,7 +646,7 @@ async def test_ws_webrtc_candidate_not_supported(
|
|||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{
|
{
|
||||||
"type": "camera/webrtc/candidate",
|
"type": "camera/webrtc/candidate",
|
||||||
"entity_id": "camera.sync",
|
"entity_id": "camera.async_no_candidate",
|
||||||
"session_id": "session_id",
|
"session_id": "session_id",
|
||||||
"candidate": {"candidate": "candidate"},
|
"candidate": {"candidate": "candidate"},
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user