mirror of
https://github.com/home-assistant/core.git
synced 2026-02-04 07:01:25 +00:00
Compare commits
13 Commits
dependabot
...
edenhaus-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32f04e6fcc | ||
|
|
b7905d441c | ||
|
|
49f6576106 | ||
|
|
da71c6b7e1 | ||
|
|
ccbff4bdc4 | ||
|
|
e80c8c1d78 | ||
|
|
b5fd8b1923 | ||
|
|
4cc324a608 | ||
|
|
01c8efb518 | ||
|
|
1fe5dc544d | ||
|
|
7583f98b0c | ||
|
|
dfdfa7fef0 | ||
|
|
7cd02988a0 |
@@ -234,7 +234,7 @@ async def _async_get_stream_image(
|
||||
height: int | None = None,
|
||||
wait_for_next_keyframe: bool = False,
|
||||
) -> bytes | None:
|
||||
if (provider := camera._webrtc_provider) and ( # noqa: SLF001
|
||||
if (provider := camera.webrtc_provider) and (
|
||||
image := await provider.async_get_image(camera, width=width, height=height)
|
||||
) is not None:
|
||||
return image
|
||||
@@ -515,6 +515,12 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return False
|
||||
return super().available
|
||||
|
||||
@final
|
||||
@property
|
||||
def webrtc_provider(self) -> CameraWebRTCProvider | None:
|
||||
"""Return the WebRTC provider."""
|
||||
return self._webrtc_provider
|
||||
|
||||
async def async_create_stream(self) -> Stream | None:
|
||||
"""Create a Stream for stream_source."""
|
||||
# There is at most one stream (a decode worker) per camera
|
||||
@@ -674,6 +680,13 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
self.__supports_stream = self.supported_features & CameraEntityFeature.STREAM
|
||||
await self.async_refresh_providers(write_state=False)
|
||||
|
||||
async def async_internal_will_remove_from_hass(self) -> None:
|
||||
"""Run when entity will be removed from hass."""
|
||||
if self._webrtc_provider:
|
||||
await self._webrtc_provider.async_unregister_camera(self)
|
||||
self._webrtc_provider = None
|
||||
await super().async_internal_will_remove_from_hass()
|
||||
|
||||
async def async_refresh_providers(self, *, write_state: bool = True) -> None:
|
||||
"""Determine if any of the registered providers are suitable for this entity.
|
||||
|
||||
@@ -690,11 +703,19 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
async_get_supported_provider
|
||||
)
|
||||
|
||||
if old_provider != new_provider:
|
||||
self._webrtc_provider = new_provider
|
||||
self._invalidate_camera_capabilities_cache()
|
||||
if write_state:
|
||||
self.async_write_ha_state()
|
||||
if old_provider == new_provider:
|
||||
return
|
||||
|
||||
if old_provider:
|
||||
await old_provider.async_unregister_camera(self)
|
||||
|
||||
if new_provider:
|
||||
await new_provider.async_register_camera(self)
|
||||
|
||||
self._webrtc_provider = new_provider
|
||||
self._invalidate_camera_capabilities_cache()
|
||||
if write_state:
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def _async_get_supported_webrtc_provider[_T](
|
||||
self, fn: Callable[[HomeAssistant, Camera], Coroutine[None, None, _T | None]]
|
||||
@@ -947,6 +968,10 @@ async def websocket_update_prefs(
|
||||
_LOGGER.error("Error setting camera preferences: %s", ex)
|
||||
connection.send_error(msg["id"], "update_failed", str(ex))
|
||||
else:
|
||||
if (camera := hass.data[DATA_COMPONENT].get_entity(entity_id)) and (
|
||||
provider := camera.webrtc_provider
|
||||
):
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
connection.send_result(msg["id"], entity_prefs)
|
||||
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ class CameraWebRTCProvider(ABC):
|
||||
@callback
|
||||
def async_close_session(self, session_id: str) -> None:
|
||||
"""Close the session."""
|
||||
return ## This is an optional method so we need a default here.
|
||||
## This is an optional method so we need a default here.
|
||||
|
||||
async def async_get_image(
|
||||
self,
|
||||
@@ -157,6 +157,27 @@ class CameraWebRTCProvider(ABC):
|
||||
"""Get an image from the camera."""
|
||||
return None
|
||||
|
||||
async def async_register_camera(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the provider is registered for a camera."""
|
||||
## This is an optional method so we need a default here.
|
||||
|
||||
async def async_unregister_camera(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the provider is unregistered for a camera."""
|
||||
## This is an optional method so we need a default here.
|
||||
|
||||
async def async_on_camera_prefs_update(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the camera preferences are updated."""
|
||||
## This is an optional method so we need a default here.
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_webrtc_provider(
|
||||
|
||||
@@ -413,12 +413,53 @@ class WebRTCProvider(CameraWebRTCProvider):
|
||||
],
|
||||
)
|
||||
|
||||
async def _update_preload_stream(self, camera: Camera) -> None:
|
||||
camera_prefs = await get_dynamic_camera_stream_settings(
|
||||
self._hass, camera.entity_id
|
||||
)
|
||||
preload_streams = await self._rest_client.preload.list()
|
||||
|
||||
if camera_prefs.preload_stream == (camera.entity_id in preload_streams):
|
||||
return
|
||||
|
||||
if camera_prefs.preload_stream:
|
||||
# We need to first add the stream source otherwise preload enabling will fail
|
||||
await self._update_stream_source(camera)
|
||||
await self._rest_client.preload.enable(camera.entity_id)
|
||||
else:
|
||||
await self._rest_client.preload.disable(camera.entity_id)
|
||||
|
||||
async def teardown(self) -> None:
|
||||
"""Tear down the provider."""
|
||||
for ws_client in self._sessions.values():
|
||||
await ws_client.close()
|
||||
self._sessions.clear()
|
||||
|
||||
async def async_register_camera(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the provider is registered for a camera."""
|
||||
await self._update_preload_stream(camera)
|
||||
|
||||
async def async_unregister_camera(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the provider is unregistered for a camera."""
|
||||
streams = await self._rest_client.streams.list()
|
||||
if streams.get(camera.entity_id):
|
||||
# If no stream exists, no need to disable preload
|
||||
# as a stream is required to enable preload
|
||||
await self._update_preload_stream(camera)
|
||||
|
||||
async def async_on_camera_prefs_update(
|
||||
self,
|
||||
camera: Camera,
|
||||
) -> None:
|
||||
"""Will be called when the camera preferences are updated."""
|
||||
await self._update_preload_stream(camera)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Go2RtcConfig:
|
||||
|
||||
@@ -74,6 +74,7 @@ _API_ALLOW_PATHS = (
|
||||
"/", # UI static page and version control
|
||||
"/api", # Main API path
|
||||
"/api/frame.jpeg", # Snapshot functionality
|
||||
"/api/preload", # Preload functionality
|
||||
"/api/schemes", # Supported stream schemes
|
||||
"/api/streams", # Stream management
|
||||
"/api/webrtc", # Webrtc functionality
|
||||
|
||||
@@ -37,7 +37,7 @@ 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, mock_turbo_jpeg
|
||||
from .common import EMPTY_8_6_JPEG, STREAM_SOURCE, SomeTestProvider, mock_turbo_jpeg
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.typing import ClientSessionGenerator, WebSocketGenerator
|
||||
@@ -1027,3 +1027,101 @@ async def test_snapshot_service_webrtc_provider(
|
||||
)
|
||||
stream_mock.async_get_image.assert_called_once()
|
||||
webrtc_get_image_mock.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
||||
async def test_provider_change_register_unregister_called(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test that register and unregister are called when provider support changes."""
|
||||
await async_setup_component(hass, "camera", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Register provider
|
||||
provider = SomeTestProvider()
|
||||
async_register_webrtc_provider(hass, provider)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
camera_obj = get_camera_from_entity_id(hass, "camera.demo_camera")
|
||||
assert camera_obj._webrtc_provider is provider
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
provider, "async_unregister_camera", AsyncMock()
|
||||
) as mock_unregister,
|
||||
patch.object(provider, "async_register_camera", AsyncMock()) as mock_register,
|
||||
):
|
||||
# Make provider unsupported
|
||||
provider._is_supported = False
|
||||
await camera_obj.async_refresh_providers()
|
||||
assert camera_obj._webrtc_provider is None
|
||||
|
||||
# Verify unregister was called
|
||||
mock_unregister.assert_called_once_with(camera_obj)
|
||||
mock_register.assert_not_called()
|
||||
|
||||
# Make provider supported again
|
||||
mock_unregister.reset_mock()
|
||||
provider._is_supported = True
|
||||
await camera_obj.async_refresh_providers()
|
||||
assert camera_obj._webrtc_provider is provider
|
||||
|
||||
# Verify register was called
|
||||
mock_register.assert_called_once_with(camera_obj)
|
||||
mock_unregister.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
||||
async def test_camera_prefs_update_calls_provider_callback(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test that async_on_camera_prefs_update is called when prefs are updated."""
|
||||
await async_setup_component(hass, "camera", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Register test provider
|
||||
await _register_test_webrtc_provider(hass)
|
||||
camera_obj = get_camera_from_entity_id(hass, "camera.demo_camera")
|
||||
assert camera_obj._webrtc_provider
|
||||
|
||||
# Patch the callback method
|
||||
with patch.object(
|
||||
camera_obj._webrtc_provider,
|
||||
"async_on_camera_prefs_update",
|
||||
AsyncMock(),
|
||||
) as mock_prefs_update:
|
||||
# Update camera preferences through WebSocket
|
||||
client = await hass_ws_client(hass)
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "camera/update_prefs",
|
||||
"entity_id": "camera.demo_camera",
|
||||
"preload_stream": True,
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
|
||||
# Assert preference was updated
|
||||
assert msg["success"]
|
||||
assert msg["result"][PREF_PRELOAD_STREAM] is True
|
||||
|
||||
# Verify callback was called
|
||||
mock_prefs_update.assert_called_once_with(camera_obj)
|
||||
|
||||
# Update another preference
|
||||
mock_prefs_update.reset_mock()
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "camera/update_prefs",
|
||||
"entity_id": "camera.demo_camera",
|
||||
"preload_stream": False,
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
assert msg["result"][PREF_PRELOAD_STREAM] is False
|
||||
|
||||
# Verify callback was called again
|
||||
mock_prefs_update.assert_called_once_with(camera_obj)
|
||||
|
||||
@@ -707,13 +707,43 @@ async def test_webrtc_provider_optional_interface(hass: HomeAssistant) -> None:
|
||||
) -> None:
|
||||
"""Handle the WebRTC candidate."""
|
||||
|
||||
camera = Mock()
|
||||
provider = OnlyRequiredInterfaceProvider()
|
||||
# Call all interface methods
|
||||
assert provider.async_is_supported("stream_source") is True
|
||||
await provider.async_handle_async_webrtc_offer(
|
||||
Mock(), "offer_sdp", "session_id", Mock()
|
||||
camera, "offer_sdp", "session_id", Mock()
|
||||
)
|
||||
await provider.async_on_webrtc_candidate(
|
||||
"session_id", RTCIceCandidateInit("candidate")
|
||||
)
|
||||
provider.async_close_session("session_id")
|
||||
await provider.async_register_camera(camera)
|
||||
await provider.async_unregister_camera(camera)
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_camera", "mock_stream_source")
|
||||
async def test_camera_unregisters_from_webrtc_provider_on_removal(
|
||||
hass: HomeAssistant,
|
||||
register_test_provider: SomeTestProvider,
|
||||
) -> None:
|
||||
"""Test camera unregisters from WebRTC provider when removed from hass."""
|
||||
camera = get_camera_from_entity_id(hass, "camera.demo_camera")
|
||||
|
||||
# Verify the provider is registered
|
||||
assert camera._webrtc_provider is not None
|
||||
assert camera._webrtc_provider == register_test_provider
|
||||
|
||||
# Mock the async_unregister_camera method
|
||||
with patch.object(
|
||||
register_test_provider, "async_unregister_camera", autospec=True
|
||||
) as mock_unregister:
|
||||
# Call async_internal_will_remove_from_hass directly to test the cleanup logic
|
||||
await camera.async_internal_will_remove_from_hass()
|
||||
|
||||
# Verify async_unregister_camera was called with the camera
|
||||
mock_unregister.assert_called_once_with(camera)
|
||||
|
||||
# Verify the provider reference was cleared
|
||||
assert camera._webrtc_provider is None
|
||||
|
||||
@@ -5,7 +5,12 @@ from pathlib import Path
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from awesomeversion import AwesomeVersion
|
||||
from go2rtc_client.rest import _SchemesClient, _StreamClient, _WebRTCClient
|
||||
from go2rtc_client.rest import (
|
||||
_PreloadClient,
|
||||
_SchemesClient,
|
||||
_StreamClient,
|
||||
_WebRTCClient,
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||
@@ -63,6 +68,7 @@ def rest_client() -> Generator[AsyncMock]:
|
||||
return_value=AwesomeVersion(RECOMMENDED_VERSION)
|
||||
)
|
||||
client.webrtc = Mock(spec_set=_WebRTCClient)
|
||||
client.preload = Mock(spec_set=_PreloadClient)
|
||||
yield client
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
_CallList([
|
||||
_Call(
|
||||
tuple(
|
||||
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws"]\n\napi:\n listen: ""\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws"]\n local_auth: true\n username: d2a0b844f4cdbe773702176c47c9a675eb0c56a0779b8f880cdb3b492ed3b1c1\n password: bc495d266a32e66ba69b9c72546e00101e04fb573f1bd08863fe4ad1aac02949\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
|
||||
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws"]\n\napi:\n listen: ""\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/preload","/api/schemes","/api/streams","/api/webrtc","/api/ws"]\n local_auth: true\n username: d2a0b844f4cdbe773702176c47c9a675eb0c56a0779b8f880cdb3b492ed3b1c1\n password: bc495d266a32e66ba69b9c72546e00101e04fb573f1bd08863fe4ad1aac02949\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
|
||||
),
|
||||
dict({
|
||||
}),
|
||||
@@ -14,7 +14,7 @@
|
||||
_CallList([
|
||||
_Call(
|
||||
tuple(
|
||||
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws","debug"]\n\napi:\n listen: ":11984"\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws","/api/config","/api/log","/api/streams.dot"]\n local_auth: true\n username: user\n password: pass\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
|
||||
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws","debug"]\n\napi:\n listen: ":11984"\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/preload","/api/schemes","/api/streams","/api/webrtc","/api/ws","/api/config","/api/log","/api/streams.dot"]\n local_auth: true\n username: user\n password: pass\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
|
||||
),
|
||||
dict({
|
||||
}),
|
||||
|
||||
@@ -62,6 +62,20 @@ OFFER_SDP = "v=0\r\no=carol 28908764872 28908764872 IN IP4 100.3.6.6\r\n..."
|
||||
ANSWER_SDP = "v=0\r\no=bob 2890844730 2890844730 IN IP4 host.example.com\r\n..."
|
||||
|
||||
|
||||
async def _setup_camera_prefs(
|
||||
hass: HomeAssistant,
|
||||
entity_id: str,
|
||||
settings: DynamicStreamSettings,
|
||||
) -> CameraPreferences:
|
||||
"""Set up camera preferences with optional orientation and preload_stream."""
|
||||
prefs = CameraPreferences(hass)
|
||||
await prefs.async_load()
|
||||
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||
|
||||
prefs._dynamic_stream_settings_by_entity_id[entity_id] = settings
|
||||
return prefs
|
||||
|
||||
|
||||
@pytest.fixture(name="has_go2rtc_entry")
|
||||
def has_go2rtc_entry_fixture() -> bool:
|
||||
"""Fixture to control if a go2rtc config entry should be created."""
|
||||
@@ -837,13 +851,9 @@ async def _test_camera_orientation(
|
||||
# Ensure go2rtc provider is initialized
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
|
||||
prefs = CameraPreferences(hass)
|
||||
await prefs.async_load()
|
||||
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||
|
||||
# Set the specific orientation for this test by directly setting the dynamic stream settings
|
||||
test_settings = DynamicStreamSettings(orientation=orientation, preload_stream=False)
|
||||
prefs._dynamic_stream_settings_by_entity_id[camera.entity_id] = test_settings
|
||||
await _setup_camera_prefs(hass, camera.entity_id, test_settings)
|
||||
|
||||
# Call the camera function that should trigger stream update
|
||||
await camera_fn(hass, camera)
|
||||
@@ -1173,3 +1183,145 @@ async def test_basic_auth_with_debug_ui(hass: HomeAssistant, server_dir: Path) -
|
||||
call_kwargs = mock_server_cls.call_args[1]
|
||||
assert call_kwargs["username"] == "test_user"
|
||||
assert call_kwargs["password"] == "test_pass"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_not_enabled_when_preference_disabled(
|
||||
hass: HomeAssistant,
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test preload is not enabled when camera has no preload preference."""
|
||||
camera = init_test_integration
|
||||
test_settings = DynamicStreamSettings(
|
||||
orientation=Orientation.NO_TRANSFORM, preload_stream=False
|
||||
)
|
||||
await _setup_camera_prefs(hass, camera.entity_id, test_settings)
|
||||
|
||||
await camera.async_handle_async_webrtc_offer(OFFER_SDP, "session_id", Mock())
|
||||
|
||||
# Verify preload was not enabled
|
||||
rest_client.preload.enable.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_disabled_on_unregister(
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test async_unregister_camera disables preload when stream exists."""
|
||||
camera = init_test_integration
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
provider = camera._webrtc_provider
|
||||
rest_client.streams.list.return_value = {
|
||||
camera.entity_id: Stream([Producer("rtsp://stream")])
|
||||
}
|
||||
rest_client.preload.list.return_value = {camera.entity_id}
|
||||
|
||||
await provider.async_unregister_camera(camera)
|
||||
|
||||
# Verify preload was disabled
|
||||
rest_client.preload.disable.assert_called_once_with(camera.entity_id)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_not_disabled_when_no_stream_exists(
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test async_unregister_camera doesn't disable preload when no stream exists."""
|
||||
camera = init_test_integration
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
provider = camera._webrtc_provider
|
||||
|
||||
await provider.async_unregister_camera(camera)
|
||||
|
||||
# Verify preload was not disabled since no stream exists
|
||||
rest_client.preload.disable.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_toggle_on_preference_update(
|
||||
hass: HomeAssistant,
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test preload is toggled when camera preferences are updated."""
|
||||
camera = init_test_integration
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
provider = camera._webrtc_provider
|
||||
test_settings = DynamicStreamSettings(
|
||||
orientation=Orientation.NO_TRANSFORM, preload_stream=True
|
||||
)
|
||||
prefs = await _setup_camera_prefs(hass, camera.entity_id, test_settings)
|
||||
|
||||
# Trigger preference update
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
|
||||
# Verify preload was enabled
|
||||
rest_client.preload.enable.assert_called_once_with(camera.entity_id)
|
||||
rest_client.preload.disable.assert_not_called()
|
||||
|
||||
# Now disable preload preference
|
||||
rest_client.preload.list.return_value = {camera.entity_id}
|
||||
rest_client.preload.enable.reset_mock()
|
||||
rest_client.preload.disable.reset_mock()
|
||||
|
||||
test_settings = DynamicStreamSettings(
|
||||
orientation=Orientation.NO_TRANSFORM, preload_stream=False
|
||||
)
|
||||
prefs._dynamic_stream_settings_by_entity_id[camera.entity_id] = test_settings
|
||||
|
||||
# Trigger preference update
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
|
||||
# Verify preload was disabled
|
||||
rest_client.preload.disable.assert_called_once_with(camera.entity_id)
|
||||
rest_client.preload.enable.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_no_change_when_already_enabled(
|
||||
hass: HomeAssistant,
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test preload enable is not called when already enabled."""
|
||||
camera = init_test_integration
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
provider = camera._webrtc_provider
|
||||
rest_client.preload.list.return_value = {camera.entity_id}
|
||||
test_settings = DynamicStreamSettings(
|
||||
orientation=Orientation.NO_TRANSFORM, preload_stream=True
|
||||
)
|
||||
await _setup_camera_prefs(hass, camera.entity_id, test_settings)
|
||||
|
||||
# Trigger preference update
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
|
||||
# Verify preload enable/disable were not called
|
||||
rest_client.preload.enable.assert_not_called()
|
||||
rest_client.preload.disable.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||
async def test_preload_no_change_when_already_disabled(
|
||||
hass: HomeAssistant,
|
||||
rest_client: AsyncMock,
|
||||
init_test_integration: MockCamera,
|
||||
) -> None:
|
||||
"""Test preload disable is not called when already disabled."""
|
||||
camera = init_test_integration
|
||||
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||
provider = camera._webrtc_provider
|
||||
test_settings = DynamicStreamSettings(
|
||||
orientation=Orientation.NO_TRANSFORM, preload_stream=False
|
||||
)
|
||||
await _setup_camera_prefs(hass, camera.entity_id, test_settings)
|
||||
|
||||
# Trigger preference update
|
||||
await provider.async_on_camera_prefs_update(camera)
|
||||
|
||||
# Verify preload enable/disable were not called
|
||||
rest_client.preload.enable.assert_not_called()
|
||||
rest_client.preload.disable.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user