Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1417 lines
46 KiB
Python
Raw Permalink Normal View History

2016-03-09 10:25:50 +01:00
"""The tests for the Universal Media player platform."""
from copy import copy
from unittest.mock import Mock, patch
import pytest
from voluptuous.error import MultipleInvalid
from homeassistant import config as hass_config
2024-04-01 11:11:59 +02:00
from homeassistant.components import input_number, input_select, media_player, switch
from homeassistant.components.media_player import (
BrowseMedia,
MediaClass,
MediaPlayerEntityFeature,
)
from homeassistant.components.universal import media_player as universal
from homeassistant.const import (
SERVICE_RELOAD,
STATE_OFF,
STATE_ON,
STATE_PAUSED,
STATE_PLAYING,
STATE_UNKNOWN,
)
from homeassistant.core import Context, HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity import EntityPlatformState
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.setup import async_setup_component
from tests.common import MockEntityPlatform, async_mock_service, get_fixture_path
CONFIG_CHILDREN_ONLY = {
"name": "test",
"platform": "universal",
"children": [
media_player.ENTITY_ID_FORMAT.format("mock1"),
media_player.ENTITY_ID_FORMAT.format("mock2"),
],
}
MOCK_BROWSE_MEDIA = BrowseMedia(
media_class=MediaClass.APP,
media_content_id="mock-id",
media_content_type="mock-type",
title="Mock Title",
can_play=False,
can_expand=True,
)
def validate_config(config):
"""Use the platform schema to validate configuration."""
validated_config = universal.PLATFORM_SCHEMA(config)
validated_config.pop("platform")
return validated_config
class MockMediaPlayer(media_player.MediaPlayerEntity):
2016-03-09 10:25:50 +01:00
"""Mock media player for testing."""
def __init__(self, hass: HomeAssistant, name: str) -> None:
2016-03-09 10:25:50 +01:00
"""Initialize the media player."""
self.hass = hass
self._name = name
self.entity_id = media_player.ENTITY_ID_FORMAT.format(name)
self._state = STATE_OFF
self._volume_level = 0
self._is_volume_muted = False
self._media_title = None
self._supported_features = 0
self._source = None
self._tracks = 12
self._media_image_url = None
self._shuffle = False
self._sound_mode = None
self._repeat = None
self.platform = MockEntityPlatform(hass)
self.service_calls = {
"turn_on": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_ON
),
"turn_off": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF
),
"mute_volume": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_MUTE
),
"set_volume_level": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET
),
"media_play": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY
),
"media_pause": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PAUSE
),
"media_stop": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_STOP
),
"media_previous_track": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PREVIOUS_TRACK
),
"media_next_track": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_NEXT_TRACK
),
"media_seek": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_SEEK
),
"play_media": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_PLAY_MEDIA
),
"volume_up": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_UP
),
"volume_down": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_DOWN
),
"media_play_pause": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY_PAUSE
),
"select_sound_mode": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOUND_MODE
),
"select_source": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE
),
"toggle": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TOGGLE
),
"clear_playlist": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_CLEAR_PLAYLIST
),
"repeat_set": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_REPEAT_SET
),
"shuffle_set": async_mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_SHUFFLE_SET
),
}
@property
def name(self):
2016-03-09 10:25:50 +01:00
"""Return the name of player."""
return self._name
@property
def state(self):
2016-03-09 10:25:50 +01:00
"""Return the state of the player."""
return self._state
@property
def volume_level(self):
"""Return the volume level of player."""
return self._volume_level
@property
def is_volume_muted(self):
2016-03-09 10:25:50 +01:00
"""Return true if the media player is muted."""
return self._is_volume_muted
@property
def supported_features(self):
"""Flag media player features that are supported."""
return MediaPlayerEntityFeature(self._supported_features)
@property
def media_image_url(self):
"""Image url of current playing media."""
return self._media_image_url
@property
def shuffle(self):
"""Return true if the media player is shuffling."""
return self._shuffle
def turn_on(self):
2016-03-09 10:25:50 +01:00
"""Mock turn_on function."""
2019-01-24 08:20:20 +01:00
self._state = None
def turn_off(self):
2016-03-09 10:25:50 +01:00
"""Mock turn_off function."""
self._state = STATE_OFF
def mute_volume(self, mute):
2016-03-09 10:25:50 +01:00
"""Mock mute function."""
self._is_volume_muted = mute
def set_volume_level(self, volume):
2016-03-09 10:25:50 +01:00
"""Mock set volume level."""
self._volume_level = volume
def media_play(self):
2016-03-09 10:25:50 +01:00
"""Mock play."""
self._state = STATE_PLAYING
def media_pause(self):
2016-03-09 10:25:50 +01:00
"""Mock pause."""
self._state = STATE_PAUSED
def select_sound_mode(self, sound_mode):
"""Set the sound mode."""
self._sound_mode = sound_mode
def select_source(self, source):
"""Set the input source."""
self._source = source
def async_toggle(self):
"""Toggle the power on the media player."""
self._state = STATE_OFF if self._state == STATE_ON else STATE_ON
def clear_playlist(self):
"""Clear players playlist."""
self._tracks = 0
def set_shuffle(self, shuffle):
"""Enable/disable shuffle mode."""
self._shuffle = shuffle
def set_repeat(self, repeat):
"""Enable/disable repeat mode."""
self._repeat = repeat
@pytest.fixture
async def mock_states(hass: HomeAssistant) -> Mock:
"""Set mock states used in tests."""
result = Mock()
result.mock_mp_1 = MockMediaPlayer(hass, "mock1")
result.mock_mp_1._platform_state = EntityPlatformState.ADDED
result.mock_mp_1.async_schedule_update_ha_state()
result.mock_mp_2 = MockMediaPlayer(hass, "mock2")
result.mock_mp_2._platform_state = EntityPlatformState.ADDED
result.mock_mp_2.async_schedule_update_ha_state()
await hass.async_block_till_done()
result.mock_mute_switch_id = switch.ENTITY_ID_FORMAT.format("mute")
hass.states.async_set(result.mock_mute_switch_id, STATE_OFF)
result.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format("state")
hass.states.async_set(result.mock_state_switch_id, STATE_OFF)
result.mock_volume_id = f"{input_number.DOMAIN}.volume_level"
hass.states.async_set(result.mock_volume_id, 0)
result.mock_source_list_id = f"{input_select.DOMAIN}.source_list"
hass.states.async_set(result.mock_source_list_id, ["dvd", "htpc"])
result.mock_source_id = f"{input_select.DOMAIN}.source"
hass.states.async_set(result.mock_source_id, "dvd")
result.mock_sound_mode_list_id = f"{input_select.DOMAIN}.sound_mode_list"
hass.states.async_set(result.mock_sound_mode_list_id, ["music", "movie"])
result.mock_sound_mode_id = f"{input_select.DOMAIN}.sound_mode"
hass.states.async_set(result.mock_sound_mode_id, "music")
result.mock_shuffle_switch_id = switch.ENTITY_ID_FORMAT.format("shuffle")
hass.states.async_set(result.mock_shuffle_switch_id, STATE_OFF)
result.mock_repeat_switch_id = switch.ENTITY_ID_FORMAT.format("repeat")
hass.states.async_set(result.mock_repeat_switch_id, STATE_OFF)
result.mock_media_image_url_id = f"{input_select.DOMAIN}.entity_picture"
hass.states.async_set(result.mock_media_image_url_id, "/local/picture.png")
return result
@pytest.fixture
def config_children_and_attr(mock_states):
"""Return configuration that references the mock states."""
return {
"name": "test",
"platform": "universal",
"children": [
media_player.ENTITY_ID_FORMAT.format("mock1"),
media_player.ENTITY_ID_FORMAT.format("mock2"),
],
"attributes": {
"is_volume_muted": mock_states.mock_mute_switch_id,
"volume_level": mock_states.mock_volume_id,
"source": mock_states.mock_source_id,
"source_list": mock_states.mock_source_list_id,
"state": mock_states.mock_state_switch_id,
"shuffle": mock_states.mock_shuffle_switch_id,
"repeat": mock_states.mock_repeat_switch_id,
"sound_mode_list": mock_states.mock_sound_mode_list_id,
"sound_mode": mock_states.mock_sound_mode_id,
"entity_picture": mock_states.mock_media_image_url_id,
},
}
async def test_config_children_only(hass: HomeAssistant) -> None:
"""Check config with only children."""
config_start = copy(CONFIG_CHILDREN_ONLY)
del config_start["platform"]
config_start["commands"] = {}
config_start["attributes"] = {}
config = validate_config(CONFIG_CHILDREN_ONLY)
assert config_start == config
async def test_config_children_and_attr(
hass: HomeAssistant, config_children_and_attr
) -> None:
"""Check config with children and attributes."""
config_start = copy(config_children_and_attr)
del config_start["platform"]
config_start["commands"] = {}
config = validate_config(config_children_and_attr)
assert config_start == config
async def test_config_no_name(hass: HomeAssistant) -> None:
"""Check config with no Name entry."""
response = True
try:
validate_config({"platform": "universal"})
except MultipleInvalid:
response = False
assert not response
2016-12-03 23:09:28 -05:00
async def test_config_bad_children(hass: HomeAssistant) -> None:
"""Check config with bad children entry."""
config_no_children = {"name": "test", "platform": "universal"}
config_bad_children = {"name": "test", "children": {}, "platform": "universal"}
2016-12-03 23:09:28 -05:00
config_no_children = validate_config(config_no_children)
2024-06-28 10:46:02 +02:00
assert config_no_children["children"] == []
config_bad_children = validate_config(config_bad_children)
2024-06-28 10:46:02 +02:00
assert config_bad_children["children"] == []
async def test_config_bad_commands(hass: HomeAssistant) -> None:
"""Check config with bad commands entry."""
config = {"name": "test", "platform": "universal"}
config = validate_config(config)
2024-06-28 10:46:02 +02:00
assert config["commands"] == {}
async def test_config_bad_attributes(hass: HomeAssistant) -> None:
"""Check config with bad attributes."""
config = {"name": "test", "platform": "universal"}
config = validate_config(config)
2024-06-28 10:46:02 +02:00
assert config["attributes"] == {}
async def test_config_bad_key(hass: HomeAssistant) -> None:
"""Check config with bad key."""
config = {"name": "test", "asdf": 5, "platform": "universal"}
config = validate_config(config)
assert "asdf" not in config
async def test_platform_setup(hass: HomeAssistant) -> None:
"""Test platform setup."""
config = {"name": "test", "platform": "universal"}
assert await async_setup_component(hass, "media_player", {"media_player": config})
await hass.async_block_till_done()
assert hass.states.async_all() != []
assert hass.states.get("media_player.test") is not None
async def test_master_state(hass: HomeAssistant) -> None:
"""Test master state property."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.master_state is None
async def test_master_state_with_attrs(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test master state property."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.master_state == STATE_OFF
hass.states.async_set(mock_states.mock_state_switch_id, STATE_ON)
assert ump.master_state == STATE_ON
async def test_master_state_with_bad_attrs(
hass: HomeAssistant, config_children_and_attr
) -> None:
"""Test master state property."""
config = copy(config_children_and_attr)
config["attributes"]["state"] = "bad.entity_id"
config = validate_config(config)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.master_state == STATE_OFF
async def test_active_child_state(hass: HomeAssistant, mock_states) -> None:
"""Test active child state property."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
2019-07-31 12:25:30 -07:00
assert ump._child_state is None
2019-07-31 12:25:30 -07:00
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert mock_states.mock_mp_1.entity_id == ump._child_state.entity_id
mock_states.mock_mp_2._state = STATE_PLAYING
mock_states.mock_mp_2.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert mock_states.mock_mp_1.entity_id == ump._child_state.entity_id
2019-07-31 12:25:30 -07:00
mock_states.mock_mp_1._state = STATE_PAUSED
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert mock_states.mock_mp_2.entity_id == ump._child_state.entity_id
mock_states.mock_mp_1._state = STATE_OFF
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert mock_states.mock_mp_2.entity_id == ump._child_state.entity_id
2019-07-31 12:25:30 -07:00
mock_states.mock_mp_1._state = "invalid_state"
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert mock_states.mock_mp_2.entity_id == ump._child_state.entity_id
async def test_name(hass: HomeAssistant) -> None:
"""Test name property."""
assert await async_setup_component(
hass, "media_player", {"media_player": CONFIG_CHILDREN_ONLY}
)
assert hass.states.get("media_player.test") is not None
2019-07-31 12:25:30 -07:00
async def test_polling(hass: HomeAssistant) -> None:
"""Test should_poll property."""
config = validate_config(CONFIG_CHILDREN_ONLY)
2019-07-31 12:25:30 -07:00
ump = universal.UniversalMediaPlayer(hass, config)
2019-07-31 12:25:30 -07:00
assert ump.should_poll is False
2019-07-31 12:25:30 -07:00
async def test_state_children_only(hass: HomeAssistant, mock_states) -> None:
"""Test media player state with only children."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert ump.state, STATE_OFF
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.state == STATE_PLAYING
mock_states.mock_mp_1._state = STATE_ON
mock_states.mock_mp_1._attr_assumed_state = True
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.assumed_state is True
async def test_state_with_children_and_attrs(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test media player with children and master state."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert ump.state == STATE_OFF
hass.states.async_set(mock_states.mock_state_switch_id, STATE_ON)
await ump.async_update()
assert ump.state == STATE_ON
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.state == STATE_PLAYING
hass.states.async_set(mock_states.mock_state_switch_id, STATE_OFF)
await ump.async_update()
assert ump.state == STATE_OFF
async def test_volume_level(hass: HomeAssistant, mock_states) -> None:
"""Test volume level property."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert ump.volume_level is None
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.volume_level == 0
mock_states.mock_mp_1._volume_level = 1
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.volume_level == 1
async def test_media_image_url(hass: HomeAssistant, mock_states) -> None:
"""Test media_image_url property."""
test_url = "test_url"
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert ump.media_image_url is None
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1._media_image_url = test_url
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
# mock_mp_1 will convert the url to the api proxy url. This test
# ensures ump passes through the same url without an additional proxy.
assert mock_states.mock_mp_1.entity_picture == ump.entity_picture
async def test_is_volume_muted_children_only(hass: HomeAssistant, mock_states) -> None:
"""Test is volume muted property w/ children only."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert not ump.is_volume_muted
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert not ump.is_volume_muted
mock_states.mock_mp_1._is_volume_muted = True
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.is_volume_muted
async def test_sound_mode_list_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test sound mode list property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.sound_mode_list == "['music', 'movie']"
hass.states.async_set(
mock_states.mock_sound_mode_list_id, ["music", "movie", "game"]
)
assert ump.sound_mode_list == "['music', 'movie', 'game']"
async def test_entity_picture_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test entity picture property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.entity_picture == "/local/picture.png"
hass.states.async_set(
mock_states.mock_sound_mode_list_id, "/local/other_picture.png"
)
assert ump.sound_mode_list == "/local/other_picture.png"
async def test_source_list_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test source list property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.source_list == "['dvd', 'htpc']"
hass.states.async_set(mock_states.mock_source_list_id, ["dvd", "htpc", "game"])
assert ump.source_list == "['dvd', 'htpc', 'game']"
async def test_sound_mode_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test sound modeproperty w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.sound_mode == "music"
hass.states.async_set(mock_states.mock_sound_mode_id, "movie")
assert ump.sound_mode == "movie"
async def test_source_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test source property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.source == "dvd"
hass.states.async_set(mock_states.mock_source_id, "htpc")
assert ump.source == "htpc"
async def test_volume_level_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test volume level property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert ump.volume_level == 0
hass.states.async_set(mock_states.mock_volume_id, 100)
assert ump.volume_level == 100
async def test_is_volume_muted_children_and_attr(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test is volume muted property w/ children and attrs."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
assert not ump.is_volume_muted
hass.states.async_set(mock_states.mock_mute_switch_id, STATE_ON)
assert ump.is_volume_muted
async def test_supported_features_children_only(
hass: HomeAssistant, mock_states
) -> None:
"""Test supported media commands with only children."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
assert ump.supported_features == 0
mock_states.mock_mp_1._supported_features = 512
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
assert ump.supported_features == 512
async def test_supported_features_children_and_cmds(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test supported media commands with children and attrs."""
config = copy(config_children_and_attr)
excmd = {"service": "media_player.test", "data": {}}
config["commands"] = {
"turn_on": excmd,
"turn_off": excmd,
"volume_up": excmd,
"volume_down": excmd,
"volume_mute": excmd,
"volume_set": excmd,
"select_sound_mode": excmd,
"select_source": excmd,
"repeat_set": excmd,
"shuffle_set": excmd,
"media_play": excmd,
"media_pause": excmd,
"media_stop": excmd,
"media_next_track": excmd,
"media_previous_track": excmd,
"toggle": excmd,
"play_media": excmd,
"clear_playlist": excmd,
}
config["browse_media_entity"] = "media_player.test"
config = validate_config(config)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
check_flags = (
MediaPlayerEntityFeature.TURN_ON
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.REPEAT_SET
| MediaPlayerEntityFeature.SHUFFLE_SET
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.PLAY
| MediaPlayerEntityFeature.PAUSE
| MediaPlayerEntityFeature.STOP
| MediaPlayerEntityFeature.NEXT_TRACK
| MediaPlayerEntityFeature.PREVIOUS_TRACK
| MediaPlayerEntityFeature.PLAY_MEDIA
| MediaPlayerEntityFeature.CLEAR_PLAYLIST
| MediaPlayerEntityFeature.BROWSE_MEDIA
)
assert check_flags == ump.supported_features
async def test_overrides(hass: HomeAssistant, config_children_and_attr) -> None:
"""Test overrides."""
config = copy(config_children_and_attr)
excmd = {"service": "test.override", "data": {}}
config["name"] = "overridden"
config["commands"] = {
"turn_on": excmd,
"turn_off": excmd,
"volume_up": excmd,
"volume_down": excmd,
"volume_mute": excmd,
"volume_set": excmd,
"select_sound_mode": excmd,
"select_source": excmd,
"repeat_set": excmd,
"shuffle_set": excmd,
"media_play": excmd,
"media_play_pause": excmd,
"media_pause": excmd,
"media_stop": excmd,
"media_next_track": excmd,
"media_previous_track": excmd,
"clear_playlist": excmd,
"play_media": excmd,
"toggle": excmd,
"entity_picture": excmd,
}
await async_setup_component(hass, "media_player", {"media_player": config})
await hass.async_block_till_done()
service = async_mock_service(hass, "test", "override")
await hass.services.async_call(
"media_player",
"turn_on",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 1
await hass.services.async_call(
"media_player",
"turn_off",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 2
await hass.services.async_call(
"media_player",
"volume_up",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 3
await hass.services.async_call(
"media_player",
"volume_down",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 4
await hass.services.async_call(
"media_player",
"volume_mute",
service_data={
"entity_id": "media_player.overridden",
"is_volume_muted": True,
},
blocking=True,
)
assert len(service) == 5
await hass.services.async_call(
"media_player",
"volume_set",
service_data={"entity_id": "media_player.overridden", "volume_level": 1},
blocking=True,
)
assert len(service) == 6
await hass.services.async_call(
"media_player",
"select_sound_mode",
service_data={
"entity_id": "media_player.overridden",
"sound_mode": "music",
},
blocking=True,
)
assert len(service) == 7
await hass.services.async_call(
"media_player",
"select_source",
service_data={"entity_id": "media_player.overridden", "source": "video1"},
blocking=True,
)
assert len(service) == 8
await hass.services.async_call(
"media_player",
"repeat_set",
service_data={"entity_id": "media_player.overridden", "repeat": "all"},
blocking=True,
)
assert len(service) == 9
await hass.services.async_call(
"media_player",
"shuffle_set",
service_data={"entity_id": "media_player.overridden", "shuffle": True},
blocking=True,
)
assert len(service) == 10
await hass.services.async_call(
"media_player",
"media_play",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 11
await hass.services.async_call(
"media_player",
"media_pause",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 12
await hass.services.async_call(
"media_player",
"media_stop",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 13
await hass.services.async_call(
"media_player",
"media_next_track",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 14
await hass.services.async_call(
"media_player",
"media_previous_track",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 15
await hass.services.async_call(
"media_player",
"clear_playlist",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 16
await hass.services.async_call(
"media_player",
"media_play_pause",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 17
await hass.services.async_call(
"media_player",
"play_media",
service_data={
"entity_id": "media_player.overridden",
"media_content_id": 1,
"media_content_type": "channel",
},
blocking=True,
)
assert len(service) == 18
await hass.services.async_call(
"media_player",
"toggle",
service_data={"entity_id": "media_player.overridden"},
blocking=True,
)
assert len(service) == 19
async def test_supported_features_play_pause(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test supported media commands with play_pause function."""
config = copy(config_children_and_attr)
excmd = {"service": "media_player.test", "data": {"entity_id": "test"}}
config["commands"] = {"media_play_pause": excmd}
config = validate_config(config)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
mock_states.mock_mp_1._state = STATE_PLAYING
mock_states.mock_mp_1.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
check_flags = MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.PAUSE
assert check_flags == ump.supported_features
async def test_service_call_no_active_child(
hass: HomeAssistant, config_children_and_attr, mock_states
) -> None:
"""Test a service call to children with no active child."""
config = validate_config(config_children_and_attr)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
mock_states.mock_mp_1._state = STATE_OFF
mock_states.mock_mp_1.async_schedule_update_ha_state()
mock_states.mock_mp_2._state = STATE_OFF
mock_states.mock_mp_2.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
await ump.async_turn_off()
assert len(mock_states.mock_mp_1.service_calls["turn_off"]) == 0
assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 0
async def test_service_call_to_child(hass: HomeAssistant, mock_states) -> None:
"""Test service calls that should be routed to a child."""
config = validate_config(CONFIG_CHILDREN_ONLY)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
mock_states.mock_mp_2._state = STATE_PLAYING
mock_states.mock_mp_2.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
await ump.async_turn_off()
assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 1
await ump.async_turn_on()
assert len(mock_states.mock_mp_2.service_calls["turn_on"]) == 1
await ump.async_mute_volume(True)
assert len(mock_states.mock_mp_2.service_calls["mute_volume"]) == 1
await ump.async_set_volume_level(0.5)
assert len(mock_states.mock_mp_2.service_calls["set_volume_level"]) == 1
await ump.async_media_play()
assert len(mock_states.mock_mp_2.service_calls["media_play"]) == 1
await ump.async_media_pause()
assert len(mock_states.mock_mp_2.service_calls["media_pause"]) == 1
await ump.async_media_stop()
assert len(mock_states.mock_mp_2.service_calls["media_stop"]) == 1
await ump.async_media_previous_track()
assert len(mock_states.mock_mp_2.service_calls["media_previous_track"]) == 1
await ump.async_media_next_track()
assert len(mock_states.mock_mp_2.service_calls["media_next_track"]) == 1
await ump.async_media_seek(100)
assert len(mock_states.mock_mp_2.service_calls["media_seek"]) == 1
await ump.async_play_media("movie", "batman")
assert len(mock_states.mock_mp_2.service_calls["play_media"]) == 1
await ump.async_volume_up()
assert len(mock_states.mock_mp_2.service_calls["volume_up"]) == 1
await ump.async_volume_down()
assert len(mock_states.mock_mp_2.service_calls["volume_down"]) == 1
await ump.async_media_play_pause()
assert len(mock_states.mock_mp_2.service_calls["media_play_pause"]) == 1
await ump.async_select_sound_mode("music")
assert len(mock_states.mock_mp_2.service_calls["select_sound_mode"]) == 1
await ump.async_select_source("dvd")
assert len(mock_states.mock_mp_2.service_calls["select_source"]) == 1
await ump.async_clear_playlist()
assert len(mock_states.mock_mp_2.service_calls["clear_playlist"]) == 1
await ump.async_set_repeat(True)
assert len(mock_states.mock_mp_2.service_calls["repeat_set"]) == 1
await ump.async_set_shuffle(True)
assert len(mock_states.mock_mp_2.service_calls["shuffle_set"]) == 1
await ump.async_toggle()
# Delegate to turn_off
assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 2
async def test_service_call_to_command(hass: HomeAssistant, mock_states) -> None:
"""Test service call to command."""
config = copy(CONFIG_CHILDREN_ONLY)
config["commands"] = {"turn_off": {"service": "test.turn_off", "data": {}}}
config = validate_config(config)
service = async_mock_service(hass, "test", "turn_off")
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
mock_states.mock_mp_2._state = STATE_PLAYING
mock_states.mock_mp_2.async_schedule_update_ha_state()
await hass.async_block_till_done()
await ump.async_update()
await ump.async_turn_off()
assert len(service) == 1
async def test_state_template(hass: HomeAssistant) -> None:
"""Test with a simple valid state template."""
hass.states.async_set("sensor.test_sensor", STATE_ON)
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"state_template": "{{ states.sensor.test_sensor.state }}",
}
},
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 2
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_ON
hass.states.async_set("sensor.test_sensor", STATE_OFF)
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_OFF
2023-03-10 12:06:50 +01:00
async def test_browse_media(hass: HomeAssistant) -> None:
"""Test browse media."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
await hass.async_block_till_done()
config = {
"name": "test",
"platform": "universal",
"children": [
"media_player.bedroom",
],
}
config = validate_config(config)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
with (
patch(
"homeassistant.components.demo.media_player.MediaPlayerEntity.supported_features",
MediaPlayerEntityFeature.BROWSE_MEDIA,
),
patch(
"homeassistant.components.demo.media_player.MediaPlayerEntity.async_browse_media",
return_value=MOCK_BROWSE_MEDIA,
2024-03-26 00:02:16 +01:00
),
):
result = await ump.async_browse_media()
assert result == MOCK_BROWSE_MEDIA
2023-03-10 12:06:50 +01:00
async def test_browse_media_override(hass: HomeAssistant) -> None:
"""Test browse media override."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
await hass.async_block_till_done()
config = {
"name": "test",
"platform": "universal",
"children": [
"media_player.mock1",
],
"browse_media_entity": "media_player.bedroom",
}
config = validate_config(config)
ump = universal.UniversalMediaPlayer(hass, config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
await ump.async_update()
with (
patch(
"homeassistant.components.demo.media_player.MediaPlayerEntity.supported_features",
MediaPlayerEntityFeature.BROWSE_MEDIA,
),
patch(
"homeassistant.components.demo.media_player.MediaPlayerEntity.async_browse_media",
return_value=MOCK_BROWSE_MEDIA,
2024-03-26 00:02:16 +01:00
),
):
result = await ump.async_browse_media()
assert result == MOCK_BROWSE_MEDIA
async def test_device_class(hass: HomeAssistant) -> None:
"""Test device_class property."""
hass.states.async_set("sensor.test_sensor", "on")
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"device_class": "tv",
}
},
)
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").attributes["device_class"] == "tv"
async def test_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Test unique_id property."""
hass.states.async_set("sensor.test_sensor", "on")
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"unique_id": "universal_master_bed_tv",
}
},
)
await hass.async_block_till_done()
assert (
entity_registry.async_get("media_player.tv").unique_id
== "universal_master_bed_tv"
)
async def test_invalid_state_template(hass: HomeAssistant) -> None:
"""Test invalid state template sets state to None."""
hass.states.async_set("sensor.test_sensor", "on")
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"state_template": "{{ states.sensor.test_sensor.state + x }}",
}
},
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 2
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_UNKNOWN
hass.states.async_set("sensor.test_sensor", "off")
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_UNKNOWN
async def test_master_state_with_template(hass: HomeAssistant) -> None:
"""Test the state_template option."""
hass.states.async_set("input_boolean.test", STATE_OFF)
hass.states.async_set("media_player.mock1", STATE_OFF)
templ = (
'{% if states.input_boolean.test.state == "off" %}on'
"{% else %}{{ states.media_player.mock1.state }}{% endif %}"
)
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"state_template": templ,
}
},
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 3
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_ON
events = []
async_track_state_change_event(
hass,
"media_player.tv",
# pylint: disable-next=unnecessary-lambda
callback(lambda event: events.append(event)),
)
context = Context()
hass.states.async_set("input_boolean.test", STATE_ON, context=context)
await hass.async_block_till_done()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_OFF
assert events[0].context == context
async def test_invalid_active_child_template(hass: HomeAssistant) -> None:
"""Test invalid active child template."""
hass.states.async_set("media_player.mock1", STATE_PLAYING)
hass.states.async_set("media_player.mock2", STATE_PAUSED)
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"children": ["media_player.mock1", "media_player.mock2"],
"active_child_template": "{{invalid.invalid}}!invalid",
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_PLAYING
async def test_active_child_template(hass: HomeAssistant) -> None:
"""Test override active child with template."""
hass.states.async_set("media_player.mock1", STATE_PLAYING)
hass.states.async_set("media_player.mock2", STATE_PAUSED)
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"children": ["media_player.mock1", "media_player.mock2"],
"active_child_template": "{{ 'media_player.mock2' }}",
}
},
)
await hass.async_block_till_done()
await hass.async_start()
hass.states.async_set("media_player.mock2", STATE_ON)
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_ON
async def test_reload(hass: HomeAssistant) -> None:
"""Test reloading the media player from yaml."""
hass.states.async_set("input_boolean.test", STATE_OFF)
hass.states.async_set("media_player.mock1", STATE_OFF)
templ = (
'{% if states.input_boolean.test.state == "off" %}on'
"{% else %}{{ states.media_player.mock1.state }}{% endif %}"
)
await async_setup_component(
hass,
"media_player",
{
"media_player": {
"platform": "universal",
"name": "tv",
"state_template": templ,
}
},
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 3
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_ON
hass.states.async_set("input_boolean.test", STATE_ON)
await hass.async_block_till_done()
assert hass.states.get("media_player.tv").state == STATE_OFF
hass.states.async_set("media_player.master_bedroom_2", STATE_OFF)
hass.states.async_set(
"remote.alexander_master_bedroom",
STATE_ON,
{
"activity_list": ["act1", "act2"],
"current_activity": "act2",
"entity_picture": "/local/picture_remote.png",
},
)
2021-11-01 20:47:05 -07:00
yaml_path = get_fixture_path("configuration.yaml", "universal")
with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
2020-08-27 13:56:20 +02:00
"universal",
SERVICE_RELOAD,
{},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 5
assert hass.states.get("media_player.tv") is None
assert hass.states.get("media_player.master_bed_tv").state == "on"
assert hass.states.get("media_player.master_bed_tv").attributes["source"] == "act2"
assert (
hass.states.get("media_player.master_bed_tv").attributes["entity_picture"]
== "/local/picture_remote.png"
)
assert (
"device_class" not in hass.states.get("media_player.master_bed_tv").attributes
)
assert "unique_id" not in hass.states.get("media_player.master_bed_tv").attributes