Remove deprecated camera frontend_stream_type (#144539)

This commit is contained in:
Robert Resch 2025-05-09 13:08:34 +02:00 committed by GitHub
parent 6350ed3415
commit c4ceb4759a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 7 additions and 130 deletions

View File

@ -61,7 +61,6 @@ from homeassistant.helpers.deprecation import (
from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.frame import ReportBehavior, report_usage
from homeassistant.helpers.network import get_url from homeassistant.helpers.network import get_url
from homeassistant.helpers.template import Template from homeassistant.helpers.template import Template
from homeassistant.helpers.typing import ConfigType, VolDictType from homeassistant.helpers.typing import ConfigType, VolDictType
@ -436,7 +435,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
CACHED_PROPERTIES_WITH_ATTR_ = { CACHED_PROPERTIES_WITH_ATTR_ = {
"brand", "brand",
"frame_interval", "frame_interval",
"frontend_stream_type",
"is_on", "is_on",
"is_recording", "is_recording",
"is_streaming", "is_streaming",
@ -456,8 +454,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
# Entity Properties # Entity Properties
_attr_brand: str | None = None _attr_brand: str | None = None
_attr_frame_interval: float = MIN_STREAM_INTERVAL _attr_frame_interval: float = MIN_STREAM_INTERVAL
# Deprecated in 2024.12. Remove in 2025.6
_attr_frontend_stream_type: StreamType | None
_attr_is_on: bool = True _attr_is_on: bool = True
_attr_is_recording: bool = False _attr_is_recording: bool = False
_attr_is_streaming: bool = False _attr_is_streaming: bool = False
@ -488,16 +484,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
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
) )
self._deprecate_attr_frontend_stream_type_logged = False
if type(self).frontend_stream_type != Camera.frontend_stream_type:
report_usage(
(
f"is overwriting the 'frontend_stream_type' property in the {type(self).__name__} class,"
" which is deprecated and will be removed in Home Assistant 2025.6, "
),
core_integration_behavior=ReportBehavior.ERROR,
exclude_integrations={DOMAIN},
)
@cached_property @cached_property
def entity_picture(self) -> str: def entity_picture(self) -> str:
@ -559,40 +545,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the interval between frames of the mjpeg stream.""" """Return the interval between frames of the mjpeg stream."""
return self._attr_frame_interval return self._attr_frame_interval
@property
def frontend_stream_type(self) -> StreamType | None:
"""Return the type of stream supported by this camera.
A camera may have a single stream type which is used to inform the
frontend which camera attributes and player to use. The default type
is to use HLS, and components can override to change the type.
"""
# Deprecated in 2024.12. Remove in 2025.6
# Use the camera_capabilities instead
if hasattr(self, "_attr_frontend_stream_type"):
if not self._deprecate_attr_frontend_stream_type_logged:
report_usage(
(
f"is setting the '_attr_frontend_stream_type' attribute in the {type(self).__name__} class,"
" which is deprecated and will be removed in Home Assistant 2025.6, "
),
core_integration_behavior=ReportBehavior.ERROR,
exclude_integrations={DOMAIN},
)
self._deprecate_attr_frontend_stream_type_logged = True
return self._attr_frontend_stream_type
if CameraEntityFeature.STREAM not in self.supported_features_compat:
return None
if (
self._webrtc_provider
or self._legacy_webrtc_provider
or self._supports_native_sync_webrtc
or self._supports_native_async_webrtc
):
return StreamType.WEB_RTC
return StreamType.HLS
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return True if entity is available.""" """Return True if entity is available."""
@ -797,9 +749,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if motion_detection_enabled := self.motion_detection_enabled: if motion_detection_enabled := self.motion_detection_enabled:
attrs["motion_detection"] = motion_detection_enabled attrs["motion_detection"] = motion_detection_enabled
if frontend_stream_type := self.frontend_stream_type:
attrs["frontend_stream_type"] = frontend_stream_type
return attrs return attrs
@callback @callback

View File

@ -39,7 +39,6 @@
'access_token': '1', 'access_token': '1',
'entity_picture': '/api/camera_proxy/camera.home?token=1', 'entity_picture': '/api/camera_proxy/camera.home?token=1',
'friendly_name': 'home', 'friendly_name': 'home',
'frontend_stream_type': <StreamType.HLS: 'hls'>,
'supported_features': <CameraEntityFeature: 2>, 'supported_features': <CameraEntityFeature: 2>,
}), }),
'context': <ANY>, 'context': <ANY>,
@ -90,7 +89,6 @@
'access_token': '1', 'access_token': '1',
'entity_picture': '/api/camera_proxy/camera.home?token=1', 'entity_picture': '/api/camera_proxy/camera.home?token=1',
'friendly_name': 'home', 'friendly_name': 'home',
'frontend_stream_type': <StreamType.HLS: 'hls'>,
'supported_features': <CameraEntityFeature: 2>, 'supported_features': <CameraEntityFeature: 2>,
}), }),
'context': <ANY>, 'context': <ANY>,

View File

@ -27,7 +27,6 @@ from homeassistant.components.camera.helper import get_camera_from_entity_id
from homeassistant.components.websocket_api import TYPE_RESULT from homeassistant.components.websocket_api import TYPE_RESULT
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
CONF_PLATFORM,
EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STARTED,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
@ -1036,27 +1035,3 @@ async def test_camera_capabilities_changing_native_support(
await hass.async_block_till_done() await hass.async_block_till_done()
await _test_capabilities(hass, hass_ws_client, cam.entity_id, set(), set()) await _test_capabilities(hass, hass_ws_client, cam.entity_id, set(), set())
@pytest.mark.usefixtures("enable_custom_integrations")
async def test_deprecated_frontend_stream_type_logs(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test using (_attr_)frontend_stream_type will log."""
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}})
await hass.async_block_till_done()
for entity_id in (
"camera.property_frontend_stream_type",
"camera.attr_frontend_stream_type",
):
camera_obj = get_camera_from_entity_id(hass, entity_id)
assert camera_obj.frontend_stream_type == StreamType.WEB_RTC
assert (
"Detected that custom integration 'test' is overwriting the 'frontend_stream_type' property in the PropertyFrontendStreamTypeCamera class, which is deprecated and will be removed in Home Assistant 2025.6,"
) in caplog.text
assert (
"Detected that custom integration 'test' is setting the '_attr_frontend_stream_type' attribute in the AttrFrontendStreamTypeCamera class, which is deprecated and will be removed in Home Assistant 2025.6,"
) in caplog.text

View File

@ -826,7 +826,6 @@ async def test_camera_multiple_streams(
assert cam is not None assert cam is not None
assert cam.state == CameraState.STREAMING assert cam.state == CameraState.STREAMING
# Prefer WebRTC over RTSP/HLS # Prefer WebRTC over RTSP/HLS
assert cam.attributes["frontend_stream_type"] == StreamType.WEB_RTC
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
assert await async_frontend_stream_types(client, "camera.my_camera") == [ assert await async_frontend_stream_types(client, "camera.my_camera") == [
StreamType.WEB_RTC StreamType.WEB_RTC
@ -905,7 +904,6 @@ async def test_webrtc_refresh_expired_stream(
cam = hass.states.get("camera.my_camera") cam = hass.states.get("camera.my_camera")
assert cam is not None assert cam is not None
assert cam.state == CameraState.STREAMING assert cam.state == CameraState.STREAMING
assert cam.attributes["frontend_stream_type"] == StreamType.WEB_RTC
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
assert await async_frontend_stream_types(client, "camera.my_camera") == [ assert await async_frontend_stream_types(client, "camera.my_camera") == [
StreamType.WEB_RTC StreamType.WEB_RTC

View File

@ -42,7 +42,6 @@
'brand': 'Netatmo', 'brand': 'Netatmo',
'entity_picture': '/api/camera_proxy/camera.front?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.front?token=1caab5c3b3',
'friendly_name': 'Front', 'friendly_name': 'Front',
'frontend_stream_type': <StreamType.HLS: 'hls'>,
'id': '12:34:56:10:b9:0e', 'id': '12:34:56:10:b9:0e',
'is_local': False, 'is_local': False,
'light_state': None, 'light_state': None,
@ -104,7 +103,6 @@
'brand': 'Netatmo', 'brand': 'Netatmo',
'entity_picture': '/api/camera_proxy/camera.hall?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.hall?token=1caab5c3b3',
'friendly_name': 'Hall', 'friendly_name': 'Hall',
'frontend_stream_type': <StreamType.HLS: 'hls'>,
'id': '12:34:56:00:f1:62', 'id': '12:34:56:00:f1:62',
'is_local': True, 'is_local': True,
'light_state': None, 'light_state': None,

View File

@ -94,7 +94,6 @@
'attribution': 'Data provided by Ring.com', 'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.front_door_live_view?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.front_door_live_view?token=1caab5c3b3',
'friendly_name': 'Front Door Live view', 'friendly_name': 'Front Door Live view',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None, 'last_video_id': None,
'supported_features': <CameraEntityFeature: 2>, 'supported_features': <CameraEntityFeature: 2>,
'video_url': None, 'video_url': None,
@ -201,7 +200,6 @@
'attribution': 'Data provided by Ring.com', 'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.front_live_view?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.front_live_view?token=1caab5c3b3',
'friendly_name': 'Front Live view', 'friendly_name': 'Front Live view',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None, 'last_video_id': None,
'supported_features': <CameraEntityFeature: 2>, 'supported_features': <CameraEntityFeature: 2>,
'video_url': None, 'video_url': None,
@ -309,7 +307,6 @@
'attribution': 'Data provided by Ring.com', 'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.internal_live_view?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.internal_live_view?token=1caab5c3b3',
'friendly_name': 'Internal Live view', 'friendly_name': 'Internal Live view',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None, 'last_video_id': None,
'supported_features': <CameraEntityFeature: 2>, 'supported_features': <CameraEntityFeature: 2>,
'video_url': None, 'video_url': None,

View File

@ -39,7 +39,6 @@
'access_token': '1caab5c3b3', 'access_token': '1caab5c3b3',
'entity_picture': '/api/camera_proxy/camera.my_camera_live_view?token=1caab5c3b3', 'entity_picture': '/api/camera_proxy/camera.my_camera_live_view?token=1caab5c3b3',
'friendly_name': 'my_camera Live view', 'friendly_name': 'my_camera Live view',
'frontend_stream_type': <StreamType.HLS: 'hls'>,
'supported_features': <CameraEntityFeature: 3>, 'supported_features': <CameraEntityFeature: 3>,
}), }),
'context': <ANY>, 'context': <ANY>,

View File

@ -12,6 +12,7 @@ from uiprotect.websocket import WebsocketState
from webrtc_models import RTCIceCandidateInit from webrtc_models import RTCIceCandidateInit
from homeassistant.components.camera import ( from homeassistant.components.camera import (
CameraCapabilities,
CameraEntityFeature, CameraEntityFeature,
CameraState, CameraState,
CameraWebRTCProvider, CameraWebRTCProvider,
@ -21,6 +22,7 @@ from homeassistant.components.camera import (
async_get_stream_source, async_get_stream_source,
async_register_webrtc_provider, async_register_webrtc_provider,
) )
from homeassistant.components.camera.helper import get_camera_from_entity_id
from homeassistant.components.unifiprotect.const import ( from homeassistant.components.unifiprotect.const import (
ATTR_BITRATE, ATTR_BITRATE,
ATTR_CHANNEL_ID, ATTR_CHANNEL_ID,
@ -345,9 +347,11 @@ async def test_webrtc_support(
camera_high_only.channels[2].is_rtsp_enabled = False camera_high_only.channels[2].is_rtsp_enabled = False
await init_entry(hass, ufp, [camera_high_only]) await init_entry(hass, ufp, [camera_high_only])
entity_id = validate_default_camera_entity(hass, camera_high_only, 0) entity_id = validate_default_camera_entity(hass, camera_high_only, 0)
state = hass.states.get(entity_id) assert hass.states.get(entity_id)
assert state camera_obj = get_camera_from_entity_id(hass, entity_id)
assert StreamType.WEB_RTC in state.attributes["frontend_stream_type"] assert camera_obj.camera_capabilities == CameraCapabilities(
{StreamType.HLS, StreamType.WEB_RTC}
)
async def test_adopt( async def test_adopt(

View File

@ -1,41 +0,0 @@
"""Provide a mock remote platform.
Call init before using it in your tests to ensure clean test data.
"""
from homeassistant.components.camera import Camera, CameraEntityFeature, StreamType
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities_callback: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Return mock entities."""
async_add_entities_callback(
[AttrFrontendStreamTypeCamera(), PropertyFrontendStreamTypeCamera()]
)
class AttrFrontendStreamTypeCamera(Camera):
"""attr frontend stream type Camera."""
_attr_name = "attr frontend stream type"
_attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM
_attr_frontend_stream_type: StreamType = StreamType.WEB_RTC
class PropertyFrontendStreamTypeCamera(Camera):
"""property frontend stream type Camera."""
_attr_name = "property frontend stream type"
_attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM
@property
def frontend_stream_type(self) -> StreamType | None:
"""Return the stream type of the camera."""
return StreamType.WEB_RTC