mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Check supported features in media player reproduce state (#70055)
This commit is contained in:
parent
7474e2f96a
commit
a5134d9ba2
@ -6,6 +6,7 @@ from collections.abc import Iterable
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_STOP,
|
||||
@ -33,6 +34,7 @@ from .const import (
|
||||
SERVICE_PLAY_MEDIA,
|
||||
SERVICE_SELECT_SOUND_MODE,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
|
||||
# mypy: allow-untyped-defs
|
||||
@ -46,6 +48,8 @@ async def _async_reproduce_states(
|
||||
reproduce_options: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Reproduce component states."""
|
||||
cur_state = hass.states.get(state.entity_id)
|
||||
features = cur_state.attributes[ATTR_SUPPORTED_FEATURES] if cur_state else 0
|
||||
|
||||
async def call_service(service: str, keys: Iterable) -> None:
|
||||
"""Call service with set of attributes given."""
|
||||
@ -59,28 +63,48 @@ async def _async_reproduce_states(
|
||||
)
|
||||
|
||||
if state.state == STATE_OFF:
|
||||
await call_service(SERVICE_TURN_OFF, [])
|
||||
if features & MediaPlayerEntityFeature.TURN_OFF:
|
||||
await call_service(SERVICE_TURN_OFF, [])
|
||||
# entities that are off have no other attributes to restore
|
||||
return
|
||||
|
||||
if state.state in (
|
||||
STATE_ON,
|
||||
STATE_PLAYING,
|
||||
STATE_IDLE,
|
||||
STATE_PAUSED,
|
||||
if (
|
||||
state.state
|
||||
in (
|
||||
STATE_ON,
|
||||
STATE_PLAYING,
|
||||
STATE_IDLE,
|
||||
STATE_PAUSED,
|
||||
)
|
||||
and features & MediaPlayerEntityFeature.TURN_ON
|
||||
):
|
||||
await call_service(SERVICE_TURN_ON, [])
|
||||
|
||||
if ATTR_MEDIA_VOLUME_LEVEL in state.attributes:
|
||||
cur_state = hass.states.get(state.entity_id)
|
||||
features = cur_state.attributes[ATTR_SUPPORTED_FEATURES] if cur_state else 0
|
||||
|
||||
if (
|
||||
ATTR_MEDIA_VOLUME_LEVEL in state.attributes
|
||||
and features & MediaPlayerEntityFeature.VOLUME_SET
|
||||
):
|
||||
await call_service(SERVICE_VOLUME_SET, [ATTR_MEDIA_VOLUME_LEVEL])
|
||||
|
||||
if ATTR_MEDIA_VOLUME_MUTED in state.attributes:
|
||||
if (
|
||||
ATTR_MEDIA_VOLUME_MUTED in state.attributes
|
||||
and features & MediaPlayerEntityFeature.VOLUME_MUTE
|
||||
):
|
||||
await call_service(SERVICE_VOLUME_MUTE, [ATTR_MEDIA_VOLUME_MUTED])
|
||||
|
||||
if ATTR_INPUT_SOURCE in state.attributes:
|
||||
if (
|
||||
ATTR_INPUT_SOURCE in state.attributes
|
||||
and features & MediaPlayerEntityFeature.SELECT_SOURCE
|
||||
):
|
||||
await call_service(SERVICE_SELECT_SOURCE, [ATTR_INPUT_SOURCE])
|
||||
|
||||
if ATTR_SOUND_MODE in state.attributes:
|
||||
if (
|
||||
ATTR_SOUND_MODE in state.attributes
|
||||
and features & MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
):
|
||||
await call_service(SERVICE_SELECT_SOUND_MODE, [ATTR_SOUND_MODE])
|
||||
|
||||
already_playing = False
|
||||
@ -88,18 +112,25 @@ async def _async_reproduce_states(
|
||||
if (ATTR_MEDIA_CONTENT_TYPE in state.attributes) and (
|
||||
ATTR_MEDIA_CONTENT_ID in state.attributes
|
||||
):
|
||||
await call_service(
|
||||
SERVICE_PLAY_MEDIA,
|
||||
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE],
|
||||
)
|
||||
if features & MediaPlayerEntityFeature.PLAY_MEDIA:
|
||||
await call_service(
|
||||
SERVICE_PLAY_MEDIA,
|
||||
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE],
|
||||
)
|
||||
already_playing = True
|
||||
|
||||
if state.state == STATE_PLAYING and not already_playing:
|
||||
if (
|
||||
not already_playing
|
||||
and state.state == STATE_PLAYING
|
||||
and features & MediaPlayerEntityFeature.PLAY
|
||||
):
|
||||
await call_service(SERVICE_MEDIA_PLAY, [])
|
||||
elif state.state == STATE_IDLE:
|
||||
await call_service(SERVICE_MEDIA_STOP, [])
|
||||
if features & MediaPlayerEntityFeature.STOP:
|
||||
await call_service(SERVICE_MEDIA_STOP, [])
|
||||
elif state.state == STATE_PAUSED:
|
||||
await call_service(SERVICE_MEDIA_PAUSE, [])
|
||||
if features & MediaPlayerEntityFeature.PAUSE:
|
||||
await call_service(SERVICE_MEDIA_PAUSE, [])
|
||||
|
||||
|
||||
async def async_reproduce_states(
|
||||
|
@ -14,9 +14,11 @@ from homeassistant.components.media_player.const import (
|
||||
SERVICE_PLAY_MEDIA,
|
||||
SERVICE_SELECT_SOUND_MODE,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
from homeassistant.components.media_player.reproduce_state import async_reproduce_states
|
||||
from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_STOP,
|
||||
@ -39,31 +41,47 @@ ENTITY_2 = "media_player.test2"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"service,state",
|
||||
"service,state,supported_feature",
|
||||
[
|
||||
(SERVICE_TURN_ON, STATE_ON),
|
||||
(SERVICE_TURN_OFF, STATE_OFF),
|
||||
(SERVICE_MEDIA_PLAY, STATE_PLAYING),
|
||||
(SERVICE_MEDIA_STOP, STATE_IDLE),
|
||||
(SERVICE_MEDIA_PAUSE, STATE_PAUSED),
|
||||
(SERVICE_TURN_ON, STATE_ON, MediaPlayerEntityFeature.TURN_ON),
|
||||
(SERVICE_TURN_OFF, STATE_OFF, MediaPlayerEntityFeature.TURN_OFF),
|
||||
(SERVICE_MEDIA_PLAY, STATE_PLAYING, MediaPlayerEntityFeature.PLAY),
|
||||
(SERVICE_MEDIA_STOP, STATE_IDLE, MediaPlayerEntityFeature.STOP),
|
||||
(SERVICE_MEDIA_PAUSE, STATE_PAUSED, MediaPlayerEntityFeature.PAUSE),
|
||||
],
|
||||
)
|
||||
async def test_state(hass, service, state):
|
||||
async def test_state(hass, service, state, supported_feature):
|
||||
"""Test that we can turn a state into a service call."""
|
||||
calls_1 = async_mock_service(hass, DOMAIN, service)
|
||||
if service != SERVICE_TURN_ON:
|
||||
async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
|
||||
# Don't support the feature won't call the service
|
||||
hass.states.async_set(ENTITY_1, "something", {ATTR_SUPPORTED_FEATURES: 0})
|
||||
await async_reproduce_states(hass, [State(ENTITY_1, state)])
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls_1) == 0
|
||||
|
||||
hass.states.async_set(
|
||||
ENTITY_1, "something", {ATTR_SUPPORTED_FEATURES: supported_feature}
|
||||
)
|
||||
await async_reproduce_states(hass, [State(ENTITY_1, state)])
|
||||
assert len(calls_1) == 1
|
||||
assert calls_1[0].data == {"entity_id": ENTITY_1}
|
||||
|
||||
|
||||
async def test_turn_on_with_mode(hass):
|
||||
"""Test that state with additional attributes call multiple services."""
|
||||
hass.states.async_set(
|
||||
ENTITY_1,
|
||||
"something",
|
||||
{
|
||||
ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.TURN_ON
|
||||
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
},
|
||||
)
|
||||
|
||||
calls_1 = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
calls_2 = async_mock_service(hass, DOMAIN, SERVICE_SELECT_SOUND_MODE)
|
||||
|
||||
@ -82,6 +100,13 @@ async def test_turn_on_with_mode(hass):
|
||||
|
||||
async def test_multiple_same_state(hass):
|
||||
"""Test that multiple states with same state gets calls."""
|
||||
for entity in ENTITY_1, ENTITY_2:
|
||||
hass.states.async_set(
|
||||
entity,
|
||||
"something",
|
||||
{ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.TURN_ON},
|
||||
)
|
||||
|
||||
calls_1 = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
|
||||
await async_reproduce_states(hass, [State(ENTITY_1, "on"), State(ENTITY_2, "on")])
|
||||
@ -96,6 +121,16 @@ async def test_multiple_same_state(hass):
|
||||
|
||||
async def test_multiple_different_state(hass):
|
||||
"""Test that multiple states with different state gets calls."""
|
||||
for entity in ENTITY_1, ENTITY_2:
|
||||
hass.states.async_set(
|
||||
entity,
|
||||
"something",
|
||||
{
|
||||
ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.TURN_ON
|
||||
| MediaPlayerEntityFeature.TURN_OFF
|
||||
},
|
||||
)
|
||||
|
||||
calls_1 = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
calls_2 = async_mock_service(hass, DOMAIN, SERVICE_TURN_OFF)
|
||||
|
||||
@ -111,6 +146,12 @@ async def test_multiple_different_state(hass):
|
||||
|
||||
async def test_state_with_context(hass):
|
||||
"""Test that context is forwarded."""
|
||||
hass.states.async_set(
|
||||
ENTITY_1,
|
||||
"something",
|
||||
{ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.TURN_ON},
|
||||
)
|
||||
|
||||
calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
|
||||
context = Context()
|
||||
@ -126,6 +167,16 @@ async def test_state_with_context(hass):
|
||||
|
||||
async def test_attribute_no_state(hass):
|
||||
"""Test that no state service call is made with none state."""
|
||||
hass.states.async_set(
|
||||
ENTITY_1,
|
||||
"something",
|
||||
{
|
||||
ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.TURN_ON
|
||||
| MediaPlayerEntityFeature.TURN_OFF
|
||||
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
},
|
||||
)
|
||||
|
||||
calls_1 = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
calls_2 = async_mock_service(hass, DOMAIN, SERVICE_TURN_OFF)
|
||||
calls_3 = async_mock_service(hass, DOMAIN, SERVICE_SELECT_SOUND_MODE)
|
||||
@ -145,16 +196,38 @@ async def test_attribute_no_state(hass):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"service,attribute",
|
||||
"service,attribute,supported_feature",
|
||||
[
|
||||
(SERVICE_VOLUME_SET, ATTR_MEDIA_VOLUME_LEVEL),
|
||||
(SERVICE_VOLUME_MUTE, ATTR_MEDIA_VOLUME_MUTED),
|
||||
(SERVICE_SELECT_SOURCE, ATTR_INPUT_SOURCE),
|
||||
(SERVICE_SELECT_SOUND_MODE, ATTR_SOUND_MODE),
|
||||
(
|
||||
SERVICE_VOLUME_SET,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
MediaPlayerEntityFeature.VOLUME_SET,
|
||||
),
|
||||
(
|
||||
SERVICE_VOLUME_MUTE,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
MediaPlayerEntityFeature.VOLUME_MUTE,
|
||||
),
|
||||
(
|
||||
SERVICE_SELECT_SOURCE,
|
||||
ATTR_INPUT_SOURCE,
|
||||
MediaPlayerEntityFeature.SELECT_SOURCE,
|
||||
),
|
||||
(
|
||||
SERVICE_SELECT_SOUND_MODE,
|
||||
ATTR_SOUND_MODE,
|
||||
MediaPlayerEntityFeature.SELECT_SOUND_MODE,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_attribute(hass, service, attribute):
|
||||
async def test_attribute(hass, service, attribute, supported_feature):
|
||||
"""Test that service call is made for each attribute."""
|
||||
hass.states.async_set(
|
||||
ENTITY_1,
|
||||
"something",
|
||||
{ATTR_SUPPORTED_FEATURES: supported_feature},
|
||||
)
|
||||
|
||||
calls_1 = async_mock_service(hass, DOMAIN, service)
|
||||
|
||||
value = "dummy"
|
||||
@ -168,7 +241,12 @@ async def test_attribute(hass, service, attribute):
|
||||
|
||||
|
||||
async def test_play_media(hass):
|
||||
"""Test that no state service call is made with none state."""
|
||||
"""Test playing media."""
|
||||
hass.states.async_set(
|
||||
ENTITY_1,
|
||||
"something",
|
||||
{ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.PLAY_MEDIA},
|
||||
)
|
||||
calls_1 = async_mock_service(hass, DOMAIN, SERVICE_PLAY_MEDIA)
|
||||
|
||||
value_1 = "dummy_1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user