mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add media player platform to Tessie (#106214)
* Add media platform * Add more props * Fix platform filename * Working * Add a test * Update test and fixture * Refactor media player properties to handle null values * Add comments * add more assertions * Fix test docstring * Use walrus instead. Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Test when media player is idle * Fix tests * Remove None type from volume_level Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Return media position only when a media duration is > 0 * Remove impossible None type * Add snapshot and freezer --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
2c2e6171e2
commit
b4f8fe8d4d
@ -21,6 +21,7 @@ PLATFORMS = [
|
||||
Platform.COVER,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.LOCK,
|
||||
Platform.MEDIA_PLAYER,
|
||||
Platform.NUMBER,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
|
109
homeassistant/components/tessie/media_player.py
Normal file
109
homeassistant/components/tessie/media_player.py
Normal file
@ -0,0 +1,109 @@
|
||||
"""Media Player platform for Tessie integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDeviceClass,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerState,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import TessieDataUpdateCoordinator
|
||||
from .entity import TessieEntity
|
||||
|
||||
STATES = {
|
||||
"Playing": MediaPlayerState.PLAYING,
|
||||
"Paused": MediaPlayerState.PAUSED,
|
||||
"Stopped": MediaPlayerState.IDLE,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the Tessie Media platform from a config entry."""
|
||||
coordinators = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(TessieMediaEntity(coordinator) for coordinator in coordinators)
|
||||
|
||||
|
||||
class TessieMediaEntity(TessieEntity, MediaPlayerEntity):
|
||||
"""Vehicle Location Media Class."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_device_class = MediaPlayerDeviceClass.SPEAKER
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: TessieDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the media player entity."""
|
||||
super().__init__(coordinator, "media")
|
||||
|
||||
@property
|
||||
def state(self) -> MediaPlayerState:
|
||||
"""State of the player."""
|
||||
return STATES.get(
|
||||
self.get("vehicle_state_media_info_media_playback_status"),
|
||||
MediaPlayerState.OFF,
|
||||
)
|
||||
|
||||
@property
|
||||
def volume_level(self) -> float:
|
||||
"""Volume level of the media player (0..1)."""
|
||||
return self.get("vehicle_state_media_info_audio_volume", 0) / self.get(
|
||||
"vehicle_state_media_info_audio_volume_max", 10.333333
|
||||
)
|
||||
|
||||
@property
|
||||
def media_duration(self) -> int | None:
|
||||
"""Duration of current playing media in seconds."""
|
||||
if duration := self.get("vehicle_state_media_info_now_playing_duration"):
|
||||
return duration / 1000
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_position(self) -> int | None:
|
||||
"""Position of current playing media in seconds."""
|
||||
# Return media position only when a media duration is > 0
|
||||
if self.get("vehicle_state_media_info_now_playing_duration"):
|
||||
return self.get("vehicle_state_media_info_now_playing_elapsed") / 1000
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_title(self) -> str | None:
|
||||
"""Title of current playing media."""
|
||||
if title := self.get("vehicle_state_media_info_now_playing_title"):
|
||||
return title
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_artist(self) -> str | None:
|
||||
"""Artist of current playing media, music track only."""
|
||||
if artist := self.get("vehicle_state_media_info_now_playing_artist"):
|
||||
return artist
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_album_name(self) -> str | None:
|
||||
"""Album name of current playing media, music track only."""
|
||||
if album := self.get("vehicle_state_media_info_now_playing_album"):
|
||||
return album
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_playlist(self) -> str | None:
|
||||
"""Title of Playlist currently playing."""
|
||||
if playlist := self.get("vehicle_state_media_info_now_playing_station"):
|
||||
return playlist
|
||||
return None
|
||||
|
||||
@property
|
||||
def source(self) -> str | None:
|
||||
"""Name of the current input source."""
|
||||
if source := self.get("vehicle_state_media_info_now_playing_source"):
|
||||
return source
|
||||
return None
|
@ -204,14 +204,14 @@
|
||||
"audio_volume": 2.3333,
|
||||
"audio_volume_increment": 0.333333,
|
||||
"audio_volume_max": 10.333333,
|
||||
"media_playback_status": "Stopped",
|
||||
"now_playing_album": "",
|
||||
"now_playing_artist": "",
|
||||
"now_playing_duration": 0,
|
||||
"now_playing_elapsed": 0,
|
||||
"media_playback_status": "Playing",
|
||||
"now_playing_album": "Album",
|
||||
"now_playing_artist": "Artist",
|
||||
"now_playing_duration": 60000,
|
||||
"now_playing_elapsed": 30000,
|
||||
"now_playing_source": "Spotify",
|
||||
"now_playing_station": "",
|
||||
"now_playing_title": ""
|
||||
"now_playing_station": "Playlist",
|
||||
"now_playing_title": "Song"
|
||||
},
|
||||
"media_state": {
|
||||
"remote_control_enabled": false
|
||||
|
@ -222,7 +222,7 @@
|
||||
"now_playing_artist": "",
|
||||
"now_playing_duration": 0,
|
||||
"now_playing_elapsed": 0,
|
||||
"now_playing_source": "Spotify",
|
||||
"now_playing_source": "",
|
||||
"now_playing_station": "",
|
||||
"now_playing_title": ""
|
||||
},
|
||||
|
61
tests/components/tessie/snapshots/test_media_player.ambr
Normal file
61
tests/components/tessie/snapshots/test_media_player.ambr
Normal file
@ -0,0 +1,61 @@
|
||||
# serializer version: 1
|
||||
# name: test_media_player_idle
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Test',
|
||||
'supported_features': <MediaPlayerEntityFeature: 0>,
|
||||
'volume_level': 0.22580323309042688,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.test',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_media_player_idle.1
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Test',
|
||||
'supported_features': <MediaPlayerEntityFeature: 0>,
|
||||
'volume_level': 0.22580323309042688,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.test',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_media_player_playing
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Test',
|
||||
'supported_features': <MediaPlayerEntityFeature: 0>,
|
||||
'volume_level': 0.22580323309042688,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.test',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'speaker',
|
||||
'friendly_name': 'Test',
|
||||
'supported_features': <MediaPlayerEntityFeature: 0>,
|
||||
'volume_level': 0.22580323309042688,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'media_player.test',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
46
tests/components/tessie/test_media_player.py
Normal file
46
tests/components/tessie/test_media_player.py
Normal file
@ -0,0 +1,46 @@
|
||||
"""Test the Tessie media player platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.tessie.coordinator import TESSIE_SYNC_INTERVAL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import (
|
||||
TEST_STATE_OF_ALL_VEHICLES,
|
||||
TEST_VEHICLE_STATE_ONLINE,
|
||||
setup_platform,
|
||||
)
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
WAIT = timedelta(seconds=TESSIE_SYNC_INTERVAL)
|
||||
|
||||
MEDIA_INFO_1 = TEST_STATE_OF_ALL_VEHICLES["results"][0]["last_state"]["vehicle_state"][
|
||||
"media_info"
|
||||
]
|
||||
MEDIA_INFO_2 = TEST_VEHICLE_STATE_ONLINE["vehicle_state"]["media_info"]
|
||||
|
||||
|
||||
async def test_media_player_idle(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, snapshot: SnapshotAssertion
|
||||
) -> None:
|
||||
"""Tests that the media player entity is correct when idle."""
|
||||
|
||||
assert len(hass.states.async_all("media_player")) == 0
|
||||
|
||||
await setup_platform(hass)
|
||||
|
||||
assert len(hass.states.async_all("media_player")) == 1
|
||||
|
||||
state = hass.states.get("media_player.test")
|
||||
assert state == snapshot
|
||||
|
||||
# Trigger coordinator refresh since it has a different fixture.
|
||||
freezer.tick(WAIT)
|
||||
async_fire_time_changed(hass)
|
||||
|
||||
state = hass.states.get("media_player.test")
|
||||
assert state == snapshot
|
Loading…
x
Reference in New Issue
Block a user