Check supported features in media player reproduce state (#70055)

This commit is contained in:
Paulus Schoutsen 2022-04-14 12:44:41 -07:00 committed by GitHub
parent 7474e2f96a
commit a5134d9ba2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 140 additions and 31 deletions

View File

@ -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(

View File

@ -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"