Remove deprecated supported features warning in MediaPlayer (#132365)

This commit is contained in:
epenet 2024-12-09 23:03:55 +01:00 committed by GitHub
parent 772b047d44
commit f2500e5a32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 20 additions and 106 deletions

View File

@ -773,19 +773,6 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag media player features that are supported.""" """Flag media player features that are supported."""
return self._attr_supported_features return self._attr_supported_features
@property
def supported_features_compat(self) -> MediaPlayerEntityFeature:
"""Return the supported features as MediaPlayerEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = MediaPlayerEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
def turn_on(self) -> None: def turn_on(self) -> None:
"""Turn the media player on.""" """Turn the media player on."""
raise NotImplementedError raise NotImplementedError
@ -925,87 +912,85 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property @property
def support_play(self) -> bool: def support_play(self) -> bool:
"""Boolean if play is supported.""" """Boolean if play is supported."""
return MediaPlayerEntityFeature.PLAY in self.supported_features_compat return MediaPlayerEntityFeature.PLAY in self.supported_features
@final @final
@property @property
def support_pause(self) -> bool: def support_pause(self) -> bool:
"""Boolean if pause is supported.""" """Boolean if pause is supported."""
return MediaPlayerEntityFeature.PAUSE in self.supported_features_compat return MediaPlayerEntityFeature.PAUSE in self.supported_features
@final @final
@property @property
def support_stop(self) -> bool: def support_stop(self) -> bool:
"""Boolean if stop is supported.""" """Boolean if stop is supported."""
return MediaPlayerEntityFeature.STOP in self.supported_features_compat return MediaPlayerEntityFeature.STOP in self.supported_features
@final @final
@property @property
def support_seek(self) -> bool: def support_seek(self) -> bool:
"""Boolean if seek is supported.""" """Boolean if seek is supported."""
return MediaPlayerEntityFeature.SEEK in self.supported_features_compat return MediaPlayerEntityFeature.SEEK in self.supported_features
@final @final
@property @property
def support_volume_set(self) -> bool: def support_volume_set(self) -> bool:
"""Boolean if setting volume is supported.""" """Boolean if setting volume is supported."""
return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
@final @final
@property @property
def support_volume_mute(self) -> bool: def support_volume_mute(self) -> bool:
"""Boolean if muting volume is supported.""" """Boolean if muting volume is supported."""
return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features_compat return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features
@final @final
@property @property
def support_previous_track(self) -> bool: def support_previous_track(self) -> bool:
"""Boolean if previous track command supported.""" """Boolean if previous track command supported."""
return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features_compat return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features
@final @final
@property @property
def support_next_track(self) -> bool: def support_next_track(self) -> bool:
"""Boolean if next track command supported.""" """Boolean if next track command supported."""
return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features_compat return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features
@final @final
@property @property
def support_play_media(self) -> bool: def support_play_media(self) -> bool:
"""Boolean if play media command supported.""" """Boolean if play media command supported."""
return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features_compat return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features
@final @final
@property @property
def support_select_source(self) -> bool: def support_select_source(self) -> bool:
"""Boolean if select source command supported.""" """Boolean if select source command supported."""
return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features_compat return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features
@final @final
@property @property
def support_select_sound_mode(self) -> bool: def support_select_sound_mode(self) -> bool:
"""Boolean if select sound mode command supported.""" """Boolean if select sound mode command supported."""
return ( return MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features
MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features_compat
)
@final @final
@property @property
def support_clear_playlist(self) -> bool: def support_clear_playlist(self) -> bool:
"""Boolean if clear playlist command supported.""" """Boolean if clear playlist command supported."""
return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features_compat return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features
@final @final
@property @property
def support_shuffle_set(self) -> bool: def support_shuffle_set(self) -> bool:
"""Boolean if shuffle is supported.""" """Boolean if shuffle is supported."""
return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features_compat return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features
@final @final
@property @property
def support_grouping(self) -> bool: def support_grouping(self) -> bool:
"""Boolean if player grouping is supported.""" """Boolean if player grouping is supported."""
return MediaPlayerEntityFeature.GROUPING in self.supported_features_compat return MediaPlayerEntityFeature.GROUPING in self.supported_features
async def async_toggle(self) -> None: async def async_toggle(self) -> None:
"""Toggle the power on the media player.""" """Toggle the power on the media player."""
@ -1034,7 +1019,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if ( if (
self.volume_level is not None self.volume_level is not None
and self.volume_level < 1 and self.volume_level < 1
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
): ):
await self.async_set_volume_level( await self.async_set_volume_level(
min(1, self.volume_level + self.volume_step) min(1, self.volume_level + self.volume_step)
@ -1052,7 +1037,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if ( if (
self.volume_level is not None self.volume_level is not None
and self.volume_level > 0 and self.volume_level > 0
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
): ):
await self.async_set_volume_level( await self.async_set_volume_level(
max(0, self.volume_level - self.volume_step) max(0, self.volume_level - self.volume_step)
@ -1095,7 +1080,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def capability_attributes(self) -> dict[str, Any]: def capability_attributes(self) -> dict[str, Any]:
"""Return capability attributes.""" """Return capability attributes."""
data: dict[str, Any] = {} data: dict[str, Any] = {}
supported_features = self.supported_features_compat supported_features = self.supported_features
if ( if (
source_list := self.source_list source_list := self.source_list
@ -1301,7 +1286,7 @@ async def websocket_browse_media(
connection.send_error(msg["id"], "entity_not_found", "Entity not found") connection.send_error(msg["id"], "entity_not_found", "Entity not found")
return return
if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features_compat: if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features:
connection.send_message( connection.send_message(
websocket_api.error_message( websocket_api.error_message(
msg["id"], ERR_NOT_SUPPORTED, "Player does not support browsing media" msg["id"], ERR_NOT_SUPPORTED, "Player does not support browsing media"

View File

@ -7,7 +7,7 @@ import asyncio
from collections import deque from collections import deque
from collections.abc import Callable, Coroutine, Iterable, Mapping from collections.abc import Callable, Coroutine, Iterable, Mapping
import dataclasses import dataclasses
from enum import Enum, IntFlag, auto from enum import Enum, auto
import functools as ft import functools as ft
import logging import logging
import math import math
@ -1639,31 +1639,6 @@ class Entity(
self.hass, integration_domain=platform_name, module=type(self).__module__ self.hass, integration_domain=platform_name, module=type(self).__module__
) )
@callback
def _report_deprecated_supported_features_values(
self, replacement: IntFlag
) -> None:
"""Report deprecated supported features values."""
if self._deprecated_supported_features_reported is True:
return
self._deprecated_supported_features_reported = True
report_issue = self._suggest_report_issue()
report_issue += (
" and reference "
"https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
)
_LOGGER.warning(
(
"Entity %s (%s) is using deprecated supported features"
" values which will be removed in HA Core 2025.1. Instead it should use"
" %s, please %s"
),
self.entity_id,
type(self),
repr(replacement),
report_issue,
)
class ToggleEntityDescription(EntityDescription, frozen_or_thawed=True): class ToggleEntityDescription(EntityDescription, frozen_or_thawed=True):
"""A class that describes toggle entities.""" """A class that describes toggle entities."""

View File

@ -129,7 +129,7 @@ def test_support_properties(property_suffix: str) -> None:
entity3 = MediaPlayerEntity() entity3 = MediaPlayerEntity()
entity3._attr_supported_features = feature entity3._attr_supported_features = feature
entity4 = MediaPlayerEntity() entity4 = MediaPlayerEntity()
entity4._attr_supported_features = all_features - feature entity4._attr_supported_features = all_features & ~feature
assert getattr(entity1, f"support_{property_suffix}") is False assert getattr(entity1, f"support_{property_suffix}") is False
assert getattr(entity2, f"support_{property_suffix}") is True assert getattr(entity2, f"support_{property_suffix}") is True
@ -447,23 +447,3 @@ async def test_get_async_get_browse_image_quoting(
url = player.get_browse_image_url("album", media_content_id) url = player.get_browse_image_url("album", media_content_id)
await client.get(url) await client.get(url)
mock_browse_image.assert_called_with("album", media_content_id, None) mock_browse_image.assert_called_with("album", media_content_id, None)
def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
"""Test deprecated supported features ints."""
class MockMediaPlayerEntity(MediaPlayerEntity):
@property
def supported_features(self) -> int:
"""Return supported features."""
return 1
entity = MockMediaPlayerEntity()
assert entity.supported_features_compat is MediaPlayerEntityFeature(1)
assert "MockMediaPlayerEntity" in caplog.text
assert "is using deprecated supported features values" in caplog.text
assert "Instead it should use" in caplog.text
assert "MediaPlayerEntityFeature.PAUSE" in caplog.text
caplog.clear()
assert entity.supported_features_compat is MediaPlayerEntityFeature(1)
assert "is using deprecated supported features values" not in caplog.text

View File

@ -4,7 +4,6 @@ import asyncio
from collections.abc import Iterable from collections.abc import Iterable
import dataclasses import dataclasses
from datetime import timedelta from datetime import timedelta
from enum import IntFlag
import logging import logging
import threading import threading
from typing import Any from typing import Any
@ -2486,31 +2485,6 @@ async def test_cached_entity_property_override(hass: HomeAssistant) -> None:
return "🤡" return "🤡"
async def test_entity_report_deprecated_supported_features_values(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test reporting deprecated supported feature values only happens once."""
ent = entity.Entity()
class MockEntityFeatures(IntFlag):
VALUE1 = 1
VALUE2 = 2
ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
assert (
"is using deprecated supported features values which will be removed"
in caplog.text
)
assert "MockEntityFeatures.VALUE2" in caplog.text
caplog.clear()
ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
assert (
"is using deprecated supported features values which will be removed"
not in caplog.text
)
async def test_remove_entity_registry( async def test_remove_entity_registry(
hass: HomeAssistant, entity_registry: er.EntityRegistry hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None: ) -> None: