diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 67c2432129f..6d65ea255c7 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -472,6 +472,8 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): _attr_state: None = None # State is determined by is_on _attr_supported_features: CameraEntityFeature = CameraEntityFeature(0) + __supports_stream: CameraEntityFeature | None = None + def __init__(self) -> None: """Initialize a camera.""" self._cache: dict[str, Any] = {} @@ -783,6 +785,9 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): async def async_internal_added_to_hass(self) -> None: """Run when entity about to be added to hass.""" await super().async_internal_added_to_hass() + self.__supports_stream = ( + self.supported_features_compat & CameraEntityFeature.STREAM + ) await self.async_refresh_providers(write_state=False) async def async_refresh_providers(self, *, write_state: bool = True) -> None: @@ -892,6 +897,21 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): return CameraCapabilities(frontend_stream_types) + @callback + def async_write_ha_state(self) -> None: + """Write the state to the state machine. + + Schedules async_refresh_providers if support of streams have changed. + """ + super().async_write_ha_state() + if self.__supports_stream != ( + supports_stream := self.supported_features_compat + & CameraEntityFeature.STREAM + ): + self.__supports_stream = supports_stream + self._invalidate_camera_capabilities_cache() + self.hass.async_create_task(self.async_refresh_providers()) + class CameraView(HomeAssistantView): """Base CameraView.""" diff --git a/tests/components/camera/conftest.py b/tests/components/camera/conftest.py index d6343959d41..f0c418711c7 100644 --- a/tests/components/camera/conftest.py +++ b/tests/components/camera/conftest.py @@ -157,7 +157,7 @@ def mock_stream_source_fixture() -> Generator[AsyncMock]: @pytest.fixture async def mock_test_webrtc_cameras(hass: HomeAssistant) -> None: - """Initialize a test WebRTC cameras.""" + """Initialize test WebRTC cameras with native RTC 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 diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 621ac8b7fb3..32024694b7e 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -1005,3 +1005,52 @@ async def test_webrtc_provider_not_added_for_native_webrtc( 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 expect_native_async_webrtc + + +@pytest.mark.usefixtures("mock_camera", "mock_stream_source") +async def test_camera_capabilities_changing_non_native_support( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, +) -> None: + """Test WebRTC camera capabilities.""" + cam = get_camera_from_entity_id(hass, "camera.demo_camera") + assert ( + cam.supported_features + == camera.CameraEntityFeature.ON_OFF | camera.CameraEntityFeature.STREAM + ) + + await _test_capabilities( + hass, + hass_ws_client, + cam.entity_id, + {StreamType.HLS}, + {StreamType.HLS, StreamType.WEB_RTC}, + ) + + cam._attr_supported_features = camera.CameraEntityFeature(0) + cam.async_write_ha_state() + await hass.async_block_till_done() + + await _test_capabilities(hass, hass_ws_client, cam.entity_id, set(), set()) + + +@pytest.mark.usefixtures("mock_test_webrtc_cameras") +@pytest.mark.parametrize(("entity_id"), ["camera.sync", "camera.async"]) +async def test_camera_capabilities_changing_native_support( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + entity_id: str, +) -> None: + """Test WebRTC camera capabilities.""" + cam = get_camera_from_entity_id(hass, entity_id) + assert cam.supported_features == camera.CameraEntityFeature.STREAM + + await _test_capabilities( + hass, hass_ws_client, cam.entity_id, {StreamType.WEB_RTC}, {StreamType.WEB_RTC} + ) + + cam._attr_supported_features = camera.CameraEntityFeature(0) + cam.async_write_ha_state() + await hass.async_block_till_done() + + await _test_capabilities(hass, hass_ws_client, cam.entity_id, set(), set())