mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add media_player support to SmartThings integration (#141296)
* Initial soundbar support * Soundbar support * Add SAMSUNG_VD_AUDIO_INPUT_SOURCE capability * Adjust setting input source * Add unit tests for media_player platform * Adjust code after merge * Adjust code after merge * Adjust code style * Adjust code style * Fix * Fix --------- Co-authored-by: Piotr Machowski <PiotrMachowski@users.noreply.github.com> Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
parent
0ddf3c794b
commit
f00fb1d9a3
@ -89,6 +89,7 @@ PLATFORMS = [
|
||||
Platform.FAN,
|
||||
Platform.LIGHT,
|
||||
Platform.LOCK,
|
||||
Platform.MEDIA_PLAYER,
|
||||
Platform.NUMBER,
|
||||
Platform.SCENE,
|
||||
Platform.SELECT,
|
||||
|
348
homeassistant/components/smartthings/media_player.py
Normal file
348
homeassistant/components/smartthings/media_player.py
Normal file
@ -0,0 +1,348 @@
|
||||
"""Support for media players through the SmartThings cloud API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pysmartthings import Attribute, Capability, Category, Command, SmartThings
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDeviceClass,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
RepeatMode,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import FullDevice, SmartThingsConfigEntry
|
||||
from .const import MAIN
|
||||
from .entity import SmartThingsEntity
|
||||
|
||||
MEDIA_PLAYER_CAPABILITIES = (
|
||||
Capability.AUDIO_MUTE,
|
||||
Capability.AUDIO_TRACK_DATA,
|
||||
Capability.AUDIO_VOLUME,
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
)
|
||||
|
||||
CONTROLLABLE_SOURCES = ["bluetooth", "wifi"]
|
||||
|
||||
DEVICE_CLASS_MAP: dict[Category | str, MediaPlayerDeviceClass] = {
|
||||
Category.NETWORK_AUDIO: MediaPlayerDeviceClass.SPEAKER,
|
||||
Category.SPEAKER: MediaPlayerDeviceClass.SPEAKER,
|
||||
Category.TELEVISION: MediaPlayerDeviceClass.TV,
|
||||
Category.RECEIVER: MediaPlayerDeviceClass.RECEIVER,
|
||||
}
|
||||
|
||||
VALUE_TO_STATE = {
|
||||
"buffering": MediaPlayerState.BUFFERING,
|
||||
"paused": MediaPlayerState.PAUSED,
|
||||
"playing": MediaPlayerState.PLAYING,
|
||||
"stopped": MediaPlayerState.IDLE,
|
||||
"fast forwarding": MediaPlayerState.BUFFERING,
|
||||
"rewinding": MediaPlayerState.BUFFERING,
|
||||
}
|
||||
|
||||
REPEAT_MODE_TO_HA = {
|
||||
"all": RepeatMode.ALL,
|
||||
"one": RepeatMode.ONE,
|
||||
"off": RepeatMode.OFF,
|
||||
}
|
||||
|
||||
HA_REPEAT_MODE_TO_SMARTTHINGS = {v: k for k, v in REPEAT_MODE_TO_HA.items()}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SmartThingsConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Add media players for a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SmartThingsMediaPlayer(entry_data.client, device)
|
||||
for device in entry_data.devices.values()
|
||||
if all(
|
||||
capability in device.status[MAIN]
|
||||
for capability in MEDIA_PLAYER_CAPABILITIES
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SmartThingsMediaPlayer(SmartThingsEntity, MediaPlayerEntity):
|
||||
"""Define a SmartThings media player."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
||||
"""Initialize the media_player class."""
|
||||
super().__init__(
|
||||
client,
|
||||
device,
|
||||
{
|
||||
Capability.AUDIO_MUTE,
|
||||
Capability.AUDIO_TRACK_DATA,
|
||||
Capability.AUDIO_VOLUME,
|
||||
Capability.MEDIA_INPUT_SOURCE,
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Capability.MEDIA_PLAYBACK_REPEAT,
|
||||
Capability.MEDIA_PLAYBACK_SHUFFLE,
|
||||
Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE,
|
||||
Capability.SWITCH,
|
||||
},
|
||||
)
|
||||
self._attr_supported_features = self._determine_features()
|
||||
self._attr_device_class = DEVICE_CLASS_MAP.get(
|
||||
device.device.components[MAIN].user_category
|
||||
or device.device.components[MAIN].manufacturer_category,
|
||||
)
|
||||
|
||||
def _determine_features(self) -> MediaPlayerEntityFeature:
|
||||
flags = MediaPlayerEntityFeature(0)
|
||||
playback_commands = self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK, Attribute.SUPPORTED_PLAYBACK_COMMANDS
|
||||
)
|
||||
if "play" in playback_commands:
|
||||
flags |= MediaPlayerEntityFeature.PLAY
|
||||
if "pause" in playback_commands:
|
||||
flags |= MediaPlayerEntityFeature.PAUSE
|
||||
if "stop" in playback_commands:
|
||||
flags |= MediaPlayerEntityFeature.STOP
|
||||
if "rewind" in playback_commands:
|
||||
flags |= MediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||
if "fastForward" in playback_commands:
|
||||
flags |= MediaPlayerEntityFeature.NEXT_TRACK
|
||||
if self.supports_capability(Capability.AUDIO_VOLUME):
|
||||
flags |= (
|
||||
MediaPlayerEntityFeature.VOLUME_SET
|
||||
| MediaPlayerEntityFeature.VOLUME_STEP
|
||||
)
|
||||
if self.supports_capability(Capability.AUDIO_MUTE):
|
||||
flags |= MediaPlayerEntityFeature.VOLUME_MUTE
|
||||
if self.supports_capability(Capability.SWITCH):
|
||||
flags |= (
|
||||
MediaPlayerEntityFeature.TURN_ON | MediaPlayerEntityFeature.TURN_OFF
|
||||
)
|
||||
if self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
|
||||
flags |= MediaPlayerEntityFeature.SELECT_SOURCE
|
||||
if self.supports_capability(Capability.MEDIA_PLAYBACK_SHUFFLE):
|
||||
flags |= MediaPlayerEntityFeature.SHUFFLE_SET
|
||||
if self.supports_capability(Capability.MEDIA_PLAYBACK_REPEAT):
|
||||
flags |= MediaPlayerEntityFeature.REPEAT_SET
|
||||
return flags
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the media player off."""
|
||||
await self.execute_device_command(
|
||||
Capability.SWITCH,
|
||||
Command.OFF,
|
||||
)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the media player on."""
|
||||
await self.execute_device_command(
|
||||
Capability.SWITCH,
|
||||
Command.ON,
|
||||
)
|
||||
|
||||
async def async_mute_volume(self, mute: bool) -> None:
|
||||
"""Mute volume."""
|
||||
await self.execute_device_command(
|
||||
Capability.AUDIO_MUTE,
|
||||
Command.SET_MUTE,
|
||||
argument="muted" if mute else "unmuted",
|
||||
)
|
||||
|
||||
async def async_set_volume_level(self, volume: float) -> None:
|
||||
"""Set volume level."""
|
||||
await self.execute_device_command(
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.SET_VOLUME,
|
||||
argument=int(volume * 100),
|
||||
)
|
||||
|
||||
async def async_volume_up(self) -> None:
|
||||
"""Increase volume."""
|
||||
await self.execute_device_command(
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.VOLUME_UP,
|
||||
)
|
||||
|
||||
async def async_volume_down(self) -> None:
|
||||
"""Decrease volume."""
|
||||
await self.execute_device_command(
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.VOLUME_DOWN,
|
||||
)
|
||||
|
||||
async def async_media_play(self) -> None:
|
||||
"""Play media."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.PLAY,
|
||||
)
|
||||
|
||||
async def async_media_pause(self) -> None:
|
||||
"""Pause media."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.PAUSE,
|
||||
)
|
||||
|
||||
async def async_media_stop(self) -> None:
|
||||
"""Stop media."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.STOP,
|
||||
)
|
||||
|
||||
async def async_media_previous_track(self) -> None:
|
||||
"""Previous track."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.REWIND,
|
||||
)
|
||||
|
||||
async def async_media_next_track(self) -> None:
|
||||
"""Next track."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.FAST_FORWARD,
|
||||
)
|
||||
|
||||
async def async_select_source(self, source: str) -> None:
|
||||
"""Select source."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_INPUT_SOURCE,
|
||||
Command.SET_INPUT_SOURCE,
|
||||
argument=source,
|
||||
)
|
||||
|
||||
async def async_set_shuffle(self, shuffle: bool) -> None:
|
||||
"""Set shuffle mode."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK_SHUFFLE,
|
||||
Command.SET_PLAYBACK_SHUFFLE,
|
||||
argument="enabled" if shuffle else "disabled",
|
||||
)
|
||||
|
||||
async def async_set_repeat(self, repeat: RepeatMode) -> None:
|
||||
"""Set repeat mode."""
|
||||
await self.execute_device_command(
|
||||
Capability.MEDIA_PLAYBACK_REPEAT,
|
||||
Command.SET_PLAYBACK_REPEAT_MODE,
|
||||
argument=HA_REPEAT_MODE_TO_SMARTTHINGS[repeat],
|
||||
)
|
||||
|
||||
@property
|
||||
def media_title(self) -> str | None:
|
||||
"""Title of current playing media."""
|
||||
if (
|
||||
track_data := self.get_attribute_value(
|
||||
Capability.AUDIO_TRACK_DATA, Attribute.AUDIO_TRACK_DATA
|
||||
)
|
||||
) is None:
|
||||
return None
|
||||
return track_data.get("title", None)
|
||||
|
||||
@property
|
||||
def media_artist(self) -> str | None:
|
||||
"""Artist of current playing media."""
|
||||
if (
|
||||
track_data := self.get_attribute_value(
|
||||
Capability.AUDIO_TRACK_DATA, Attribute.AUDIO_TRACK_DATA
|
||||
)
|
||||
) is None:
|
||||
return None
|
||||
return track_data.get("artist")
|
||||
|
||||
@property
|
||||
def state(self) -> MediaPlayerState | None:
|
||||
"""State of the media player."""
|
||||
if self.supports_capability(Capability.SWITCH):
|
||||
if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on":
|
||||
if (
|
||||
self.source is not None
|
||||
and self.source in CONTROLLABLE_SOURCES
|
||||
and self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
|
||||
)
|
||||
in VALUE_TO_STATE
|
||||
):
|
||||
return VALUE_TO_STATE[
|
||||
self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
|
||||
)
|
||||
]
|
||||
return MediaPlayerState.ON
|
||||
return MediaPlayerState.OFF
|
||||
return VALUE_TO_STATE[
|
||||
self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
|
||||
)
|
||||
]
|
||||
|
||||
@property
|
||||
def is_volume_muted(self) -> bool:
|
||||
"""Returns if the volume is muted."""
|
||||
return (
|
||||
self.get_attribute_value(Capability.AUDIO_MUTE, Attribute.MUTE) == "muted"
|
||||
)
|
||||
|
||||
@property
|
||||
def volume_level(self) -> float:
|
||||
"""Volume level."""
|
||||
return self.get_attribute_value(Capability.AUDIO_VOLUME, Attribute.VOLUME) / 100
|
||||
|
||||
@property
|
||||
def source(self) -> str | None:
|
||||
"""Input source."""
|
||||
if self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
|
||||
return self.get_attribute_value(
|
||||
Capability.MEDIA_INPUT_SOURCE, Attribute.INPUT_SOURCE
|
||||
)
|
||||
if self.supports_capability(Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE):
|
||||
return self.get_attribute_value(
|
||||
Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE, Attribute.INPUT_SOURCE
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def source_list(self) -> list[str] | None:
|
||||
"""List of input sources."""
|
||||
if self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
|
||||
return self.get_attribute_value(
|
||||
Capability.MEDIA_INPUT_SOURCE, Attribute.SUPPORTED_INPUT_SOURCES
|
||||
)
|
||||
if self.supports_capability(Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE):
|
||||
return self.get_attribute_value(
|
||||
Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE,
|
||||
Attribute.SUPPORTED_INPUT_SOURCES,
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def shuffle(self) -> bool | None:
|
||||
"""Returns if shuffle mode is set."""
|
||||
if self.supports_capability(Capability.MEDIA_PLAYBACK_SHUFFLE):
|
||||
return (
|
||||
self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK_SHUFFLE, Attribute.PLAYBACK_SHUFFLE
|
||||
)
|
||||
== "enabled"
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def repeat(self) -> RepeatMode | None:
|
||||
"""Returns if repeat mode is set."""
|
||||
if self.supports_capability(Capability.MEDIA_PLAYBACK_REPEAT):
|
||||
return REPEAT_MODE_TO_HA[
|
||||
self.get_attribute_value(
|
||||
Capability.MEDIA_PLAYBACK_REPEAT, Attribute.PLAYBACK_REPEAT_MODE
|
||||
)
|
||||
]
|
||||
return None
|
@ -140,6 +140,7 @@ def mock_smartthings() -> Generator[AsyncMock]:
|
||||
"tplink_p110",
|
||||
"ikea_kadrilj",
|
||||
"aux_ac",
|
||||
"hw_q80r_soundbar",
|
||||
]
|
||||
)
|
||||
def device_fixture(
|
||||
|
@ -0,0 +1,173 @@
|
||||
{
|
||||
"components": {
|
||||
"main": {
|
||||
"mediaPlayback": {
|
||||
"supportedPlaybackCommands": {
|
||||
"value": ["play", "pause", "stop"],
|
||||
"timestamp": "2025-03-23T01:10:02.207Z"
|
||||
},
|
||||
"playbackStatus": {
|
||||
"value": "playing",
|
||||
"timestamp": "2025-03-23T01:19:44.622Z"
|
||||
}
|
||||
},
|
||||
"samsungvd.groupInfo": {
|
||||
"role": {
|
||||
"value": "none",
|
||||
"timestamp": "2025-03-23T01:17:10.965Z"
|
||||
},
|
||||
"channel": {
|
||||
"value": "all",
|
||||
"timestamp": "2025-03-23T01:17:10.965Z"
|
||||
},
|
||||
"masterName": {
|
||||
"value": "",
|
||||
"timestamp": "2025-03-23T01:17:10.965Z"
|
||||
},
|
||||
"status": {
|
||||
"value": "single",
|
||||
"timestamp": "2025-03-23T01:17:10.965Z"
|
||||
}
|
||||
},
|
||||
"audioVolume": {
|
||||
"volume": {
|
||||
"value": 1,
|
||||
"unit": "%",
|
||||
"timestamp": "2025-03-23T01:17:13.754Z"
|
||||
}
|
||||
},
|
||||
"ocf": {
|
||||
"st": {
|
||||
"value": "NONE",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mndt": {
|
||||
"value": "2018-01-01",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnfv": {
|
||||
"value": "HW-Q80RWWB-1012.6",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnhw": {
|
||||
"value": "0-0",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"di": {
|
||||
"value": "afcf3b91-48fe-4c3b-ab44-ddff2a0a6577",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnsl": {
|
||||
"value": "http://www.samsung.com/sec/audio-video/",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"dmv": {
|
||||
"value": "res.1.1.0,sh.1.1.0",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"n": {
|
||||
"value": "[AV] Samsung Soundbar Q80R",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnmo": {
|
||||
"value": "Q80R",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"vid": {
|
||||
"value": "VD-NetworkAudio-001S",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnmn": {
|
||||
"value": "Samsung Electronics",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnml": {
|
||||
"value": "http://www.samsung.com",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnpv": {
|
||||
"value": "Tizen 4.0",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"mnos": {
|
||||
"value": "4.1.10",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"pi": {
|
||||
"value": "afcf3b91-48fe-4c3b-ab44-ddff2a0a6577",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
},
|
||||
"icv": {
|
||||
"value": "core.1.1.0",
|
||||
"timestamp": "2024-12-18T21:07:25.406Z"
|
||||
}
|
||||
},
|
||||
"mediaInputSource": {
|
||||
"supportedInputSources": {
|
||||
"value": ["wifi", "bluetooth", "HDMI1", "HDMI2", "digital"],
|
||||
"timestamp": "2025-03-23T01:18:01.663Z"
|
||||
},
|
||||
"inputSource": {
|
||||
"value": "wifi",
|
||||
"timestamp": "2025-03-23T01:18:01.663Z"
|
||||
}
|
||||
},
|
||||
"refresh": {},
|
||||
"audioNotification": {},
|
||||
"audioMute": {
|
||||
"mute": {
|
||||
"value": "unmuted",
|
||||
"timestamp": "2025-03-23T01:17:11.024Z"
|
||||
}
|
||||
},
|
||||
"execute": {
|
||||
"data": {
|
||||
"value": {
|
||||
"payload": {
|
||||
"rt": ["x.com.samsung.networkaudio.soundmode"],
|
||||
"if": ["oic.if.a", "oic.if.baseline"],
|
||||
"x.com.samsung.networkaudio.soundmode": "standard"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"href": "/sec/networkaudio/soundmode"
|
||||
},
|
||||
"timestamp": "2023-07-16T23:16:55.582Z"
|
||||
}
|
||||
},
|
||||
"samsungvd.audioInputSource": {
|
||||
"supportedInputSources": {
|
||||
"value": ["wifi", "bluetooth", "HDMI1", "HDMI2", "digital"],
|
||||
"timestamp": "2025-03-23T01:18:01.663Z"
|
||||
},
|
||||
"inputSource": {
|
||||
"value": "wificp",
|
||||
"timestamp": "2025-03-23T01:18:01.663Z"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"switch": {
|
||||
"value": "on",
|
||||
"timestamp": "2025-03-23T01:19:44.837Z"
|
||||
}
|
||||
},
|
||||
"audioTrackData": {
|
||||
"totalTime": {
|
||||
"value": null,
|
||||
"timestamp": "2020-07-30T16:09:09.109Z"
|
||||
},
|
||||
"audioTrackData": {
|
||||
"value": {
|
||||
"title": "Never Gonna Give You Up",
|
||||
"artist": "Rick Astley"
|
||||
},
|
||||
"timestamp": "2025-03-23T01:19:15.067Z"
|
||||
},
|
||||
"elapsedTime": {
|
||||
"value": null,
|
||||
"timestamp": "2020-07-30T16:09:09.109Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"deviceId": "afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
"name": "[AV] Samsung Soundbar Q80R",
|
||||
"label": "Soundbar",
|
||||
"manufacturerName": "Samsung Electronics",
|
||||
"presentationId": "VD-NetworkAudio-001S",
|
||||
"deviceManufacturerCode": "Samsung Electronics",
|
||||
"locationId": "c7f8e400-0000-1111-2222-76463f4eb484",
|
||||
"ownerId": "bd0d9288-0000-1111-2222-68310a42a709",
|
||||
"roomId": "be09ff51-0000-1111-2222-e48e2dab37fd",
|
||||
"deviceTypeName": "Samsung OCF Network Audio Player",
|
||||
"components": [
|
||||
{
|
||||
"id": "main",
|
||||
"label": "Soundbar",
|
||||
"capabilities": [
|
||||
{
|
||||
"id": "ocf",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "execute",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "refresh",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "switch",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "audioVolume",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "audioMute",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "audioTrackData",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "mediaInputSource",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungvd.audioInputSource",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "mediaPlayback",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "audioNotification",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungvd.groupInfo",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"name": "NetworkAudio",
|
||||
"categoryType": "manufacturer"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"createTime": "2020-10-19T01:35:08Z",
|
||||
"profile": {
|
||||
"id": "c1036d88-000-1111-2222-a361463fd53f"
|
||||
},
|
||||
"ocf": {
|
||||
"ocfDeviceType": "oic.d.networkaudio",
|
||||
"name": "[AV] Samsung Soundbar Q80R",
|
||||
"specVersion": "core.1.1.0",
|
||||
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
|
||||
"manufacturerName": "Samsung Electronics",
|
||||
"modelNumber": "Q80R",
|
||||
"platformVersion": "Tizen 4.0",
|
||||
"platformOS": "4.1.10",
|
||||
"hwVersion": "0-0",
|
||||
"firmwareVersion": "HW-Q80RWWB-1012.6",
|
||||
"vendorId": "VD-NetworkAudio-001S",
|
||||
"vendorResourceClientServerVersion": "1.2",
|
||||
"locale": "KO",
|
||||
"lastSignupTime": "2021-01-16T07:05:02.184545Z",
|
||||
"transferCandidate": false,
|
||||
"additionalAuthCodeRequired": false
|
||||
},
|
||||
"type": "OCF",
|
||||
"restrictionTier": 0,
|
||||
"allowed": null,
|
||||
"executionContext": "CLOUD",
|
||||
"relationships": []
|
||||
}
|
||||
],
|
||||
"_links": {}
|
||||
}
|
@ -1157,6 +1157,39 @@
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[hw_q80r_soundbar]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': 'https://account.smartthings.com',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': '0-0',
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'smartthings',
|
||||
'afcf3b91-0000-1111-2222-ddff2a0a6577',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Samsung Electronics',
|
||||
'model': 'Q80R',
|
||||
'model_id': None,
|
||||
'name': 'Soundbar',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': 'HW-Q80RWWB-1012.6',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[ikea_kadrilj]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
|
233
tests/components/smartthings/snapshots/test_media_player.ambr
Normal file
233
tests/components/smartthings/snapshots/test_media_player.ambr
Normal file
@ -0,0 +1,233 @@
|
||||
# serializer version: 1
|
||||
# name: test_all_entities[hw_q80r_soundbar][media_player.soundbar-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'source_list': list([
|
||||
'wifi',
|
||||
'bluetooth',
|
||||
'HDMI1',
|
||||
'HDMI2',
|
||||
'digital',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'media_player',
|
||||
'entity_category': None,
|
||||
'entity_id': 'media_player.soundbar',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <MediaPlayerEntityFeature: 23949>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][media_player.soundbar-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Soundbar',
|
||||
'is_volume_muted': False,
|
||||
'media_artist': 'Rick Astley',
|
||||
'media_title': 'Never Gonna Give You Up',
|
||||
'source': 'wifi',
|
||||
'source_list': list([
|
||||
'wifi',
|
||||
'bluetooth',
|
||||
'HDMI1',
|
||||
'HDMI2',
|
||||
'digital',
|
||||
]),
|
||||
'supported_features': <MediaPlayerEntityFeature: 23949>,
|
||||
'volume_level': 0.01,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.soundbar',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'playing',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[im_speaker_ai_0001][media_player.galaxy_home_mini-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'media_player',
|
||||
'entity_category': None,
|
||||
'entity_id': 'media_player.galaxy_home_mini',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <MediaPlayerEntityFeature: 318477>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[im_speaker_ai_0001][media_player.galaxy_home_mini-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Galaxy Home Mini',
|
||||
'is_volume_muted': False,
|
||||
'repeat': <RepeatMode.OFF: 'off'>,
|
||||
'shuffle': False,
|
||||
'supported_features': <MediaPlayerEntityFeature: 318477>,
|
||||
'volume_level': 0.52,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.galaxy_home_mini',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sonos_player][media_player.elliots_rum-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'media_player',
|
||||
'entity_category': None,
|
||||
'entity_id': 'media_player.elliots_rum',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <MediaPlayerEntityFeature: 21517>,
|
||||
'translation_key': None,
|
||||
'unique_id': 'c85fced9-c474-4a47-93c2-037cc7829536',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sonos_player][media_player.elliots_rum-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Elliots Rum',
|
||||
'is_volume_muted': False,
|
||||
'media_artist': 'David Guetta',
|
||||
'media_title': 'Forever Young',
|
||||
'supported_features': <MediaPlayerEntityFeature: 21517>,
|
||||
'volume_level': 0.15,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.elliots_rum',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'playing',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[vd_network_audio_002s][media_player.soundbar_living-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'media_player',
|
||||
'entity_category': None,
|
||||
'entity_id': 'media_player.soundbar_living',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <MediaPlayerDeviceClass.SPEAKER: 'speaker'>,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <MediaPlayerEntityFeature: 21901>,
|
||||
'translation_key': None,
|
||||
'unique_id': '0d94e5db-8501-2355-eb4f-214163702cac',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[vd_network_audio_002s][media_player.soundbar_living-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Soundbar Living',
|
||||
'is_volume_muted': False,
|
||||
'media_artist': '',
|
||||
'media_title': '',
|
||||
'source': 'HDMI1',
|
||||
'supported_features': <MediaPlayerEntityFeature: 21901>,
|
||||
'volume_level': 0.17,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.soundbar_living',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
@ -7193,6 +7193,182 @@
|
||||
'state': '19.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_input_source-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'wifi',
|
||||
'bluetooth',
|
||||
'hdmi1',
|
||||
'hdmi2',
|
||||
'digital',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.soundbar_media_input_source',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Media input source',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'media_input_source',
|
||||
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577.inputSource',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_input_source-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Soundbar Media input source',
|
||||
'options': list([
|
||||
'wifi',
|
||||
'bluetooth',
|
||||
'hdmi1',
|
||||
'hdmi2',
|
||||
'digital',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.soundbar_media_input_source',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'wifi',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_playback_status-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'paused',
|
||||
'playing',
|
||||
'stopped',
|
||||
'fast_forwarding',
|
||||
'rewinding',
|
||||
'buffering',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.soundbar_media_playback_status',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Media playback status',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'media_playback_status',
|
||||
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577.playbackStatus',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_playback_status-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Soundbar Media playback status',
|
||||
'options': list([
|
||||
'paused',
|
||||
'playing',
|
||||
'stopped',
|
||||
'fast_forwarding',
|
||||
'rewinding',
|
||||
'buffering',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.soundbar_media_playback_status',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'playing',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_volume-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.soundbar_volume',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Volume',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'audio_volume',
|
||||
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577.volume',
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_volume-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Soundbar Volume',
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.soundbar_volume',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[ikea_kadrilj][sensor.kitchen_ikea_kadrilj_window_blind_battery-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
@ -610,6 +610,53 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][switch.soundbar-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.soundbar',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[hw_q80r_soundbar][switch.soundbar-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Soundbar',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.soundbar',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensibo_airconditioner_1][switch.office-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
432
tests/components/smartthings/test_media_player.py
Normal file
432
tests/components/smartthings/test_media_player.py
Normal file
@ -0,0 +1,432 @@
|
||||
"""Test for the SmartThings media player platform."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from pysmartthings import Attribute, Capability, Command, Status
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_INPUT_SOURCE,
|
||||
ATTR_MEDIA_REPEAT,
|
||||
ATTR_MEDIA_SHUFFLE,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
RepeatMode,
|
||||
)
|
||||
from homeassistant.components.smartthings.const import MAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||
SERVICE_MEDIA_STOP,
|
||||
SERVICE_REPEAT_SET,
|
||||
SERVICE_SHUFFLE_SET,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
SERVICE_VOLUME_DOWN,
|
||||
SERVICE_VOLUME_MUTE,
|
||||
SERVICE_VOLUME_SET,
|
||||
SERVICE_VOLUME_UP,
|
||||
STATE_OFF,
|
||||
STATE_PLAYING,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration, snapshot_smartthings_entities, trigger_update
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_all_entities(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test all entities."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
snapshot_smartthings_entities(
|
||||
hass, entity_registry, snapshot, Platform.MEDIA_PLAYER
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
@pytest.mark.parametrize(
|
||||
("action", "command"),
|
||||
[
|
||||
(SERVICE_TURN_ON, Command.ON),
|
||||
(SERVICE_TURN_OFF, Command.OFF),
|
||||
],
|
||||
)
|
||||
async def test_turn_on_off(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
action: str,
|
||||
command: Command,
|
||||
) -> None:
|
||||
"""Test media player turn on and off command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
action,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577", Capability.SWITCH, command, MAIN
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
@pytest.mark.parametrize(
|
||||
("muted", "argument"),
|
||||
[
|
||||
(True, "muted"),
|
||||
(False, "unmuted"),
|
||||
],
|
||||
)
|
||||
async def test_mute_unmute(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
muted: bool,
|
||||
argument: str,
|
||||
) -> None:
|
||||
"""Test media player mute and unmute command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_VOLUME_MUTE,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar", ATTR_MEDIA_VOLUME_MUTED: muted},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.AUDIO_MUTE,
|
||||
Command.SET_MUTE,
|
||||
MAIN,
|
||||
argument=argument,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_set_volume_level(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player set volume level command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_VOLUME_SET,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar", ATTR_MEDIA_VOLUME_LEVEL: 0.31},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.SET_VOLUME,
|
||||
MAIN,
|
||||
argument=31,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_volume_up(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player increase volume level command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_VOLUME_UP,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.VOLUME_UP,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_volume_down(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player decrease volume level command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_VOLUME_DOWN,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.AUDIO_VOLUME,
|
||||
Command.VOLUME_DOWN,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_media_play(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player play command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.PLAY,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_media_pause(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player pause command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.PAUSE,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_media_stop(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player stop command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_MEDIA_STOP,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.STOP,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_media_previous_track(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player previous track command."""
|
||||
devices.get_device_status.return_value[MAIN][Capability.MEDIA_PLAYBACK] = {
|
||||
Attribute.SUPPORTED_PLAYBACK_COMMANDS: Status(["rewind"])
|
||||
}
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.REWIND,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_media_next_track(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player next track command."""
|
||||
devices.get_device_status.return_value[MAIN][Capability.MEDIA_PLAYBACK] = {
|
||||
Attribute.SUPPORTED_PLAYBACK_COMMANDS: Status(["fastForward"])
|
||||
}
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK,
|
||||
Command.FAST_FORWARD,
|
||||
MAIN,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_select_source(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test media player stop command."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar", ATTR_INPUT_SOURCE: "digital"},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_INPUT_SOURCE,
|
||||
Command.SET_INPUT_SOURCE,
|
||||
MAIN,
|
||||
"digital",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
@pytest.mark.parametrize(
|
||||
("shuffle", "argument"),
|
||||
[
|
||||
(True, "enabled"),
|
||||
(False, "disabled"),
|
||||
],
|
||||
)
|
||||
async def test_media_shuffle_on_off(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
shuffle: bool,
|
||||
argument: bool,
|
||||
) -> None:
|
||||
"""Test media player media shuffle command."""
|
||||
devices.get_device_status.return_value[MAIN][Capability.MEDIA_PLAYBACK_SHUFFLE] = {
|
||||
Attribute.PLAYBACK_SHUFFLE: Status(True)
|
||||
}
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_SHUFFLE_SET,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar", ATTR_MEDIA_SHUFFLE: shuffle},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK_SHUFFLE,
|
||||
Command.SET_PLAYBACK_SHUFFLE,
|
||||
MAIN,
|
||||
argument=argument,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
@pytest.mark.parametrize(
|
||||
("repeat", "argument"),
|
||||
[
|
||||
(RepeatMode.OFF, "off"),
|
||||
(RepeatMode.ONE, "one"),
|
||||
(RepeatMode.ALL, "all"),
|
||||
],
|
||||
)
|
||||
async def test_media_repeat_mode(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
repeat: RepeatMode,
|
||||
argument: bool,
|
||||
) -> None:
|
||||
"""Test media player repeat mode command."""
|
||||
devices.get_device_status.return_value[MAIN][Capability.MEDIA_PLAYBACK_REPEAT] = {
|
||||
Attribute.REPEAT_MODE: Status("one")
|
||||
}
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_REPEAT_SET,
|
||||
{ATTR_ENTITY_ID: "media_player.soundbar", ATTR_MEDIA_REPEAT: repeat},
|
||||
blocking=True,
|
||||
)
|
||||
devices.execute_device_command.assert_called_once_with(
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.MEDIA_PLAYBACK_REPEAT,
|
||||
Command.SET_PLAYBACK_REPEAT_MODE,
|
||||
MAIN,
|
||||
argument=argument,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", ["hw_q80r_soundbar"])
|
||||
async def test_state_update(
|
||||
hass: HomeAssistant,
|
||||
devices: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test state update."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("media_player.soundbar").state == STATE_PLAYING
|
||||
|
||||
await trigger_update(
|
||||
hass,
|
||||
devices,
|
||||
"afcf3b91-0000-1111-2222-ddff2a0a6577",
|
||||
Capability.SWITCH,
|
||||
Attribute.SWITCH,
|
||||
"off",
|
||||
)
|
||||
|
||||
assert hass.states.get("media_player.soundbar").state == STATE_OFF
|
Loading…
x
Reference in New Issue
Block a user