mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Support DirecTV music channels with extended meta (#34228)
This commit is contained in:
parent
446c7349ff
commit
1f4cdda234
@ -8,6 +8,7 @@ from homeassistant.components.media_player import MediaPlayerDevice
|
|||||||
from homeassistant.components.media_player.const import (
|
from homeassistant.components.media_player.const import (
|
||||||
MEDIA_TYPE_CHANNEL,
|
MEDIA_TYPE_CHANNEL,
|
||||||
MEDIA_TYPE_MOVIE,
|
MEDIA_TYPE_MOVIE,
|
||||||
|
MEDIA_TYPE_MUSIC,
|
||||||
MEDIA_TYPE_TVSHOW,
|
MEDIA_TYPE_TVSHOW,
|
||||||
SUPPORT_NEXT_TRACK,
|
SUPPORT_NEXT_TRACK,
|
||||||
SUPPORT_PAUSE,
|
SUPPORT_PAUSE,
|
||||||
@ -34,6 +35,8 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
KNOWN_MEDIA_TYPES = [MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW]
|
||||||
|
|
||||||
SUPPORT_DTV = (
|
SUPPORT_DTV = (
|
||||||
SUPPORT_PAUSE
|
SUPPORT_PAUSE
|
||||||
| SUPPORT_TURN_ON
|
| SUPPORT_TURN_ON
|
||||||
@ -177,8 +180,7 @@ class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerDevice):
|
|||||||
if self._is_standby or self._program is None:
|
if self._is_standby or self._program is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
known_types = [MEDIA_TYPE_MOVIE, MEDIA_TYPE_TVSHOW]
|
if self._program.program_type in KNOWN_MEDIA_TYPES:
|
||||||
if self._program.program_type in known_types:
|
|
||||||
return self._program.program_type
|
return self._program.program_type
|
||||||
|
|
||||||
return MEDIA_TYPE_MOVIE
|
return MEDIA_TYPE_MOVIE
|
||||||
@ -213,8 +215,27 @@ class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerDevice):
|
|||||||
if self._is_standby or self._program is None:
|
if self._is_standby or self._program is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if self.media_content_type == MEDIA_TYPE_MUSIC:
|
||||||
|
return self._program.music_title
|
||||||
|
|
||||||
return self._program.title
|
return self._program.title
|
||||||
|
|
||||||
|
@property
|
||||||
|
def media_artist(self):
|
||||||
|
"""Artist of current playing media, music track only."""
|
||||||
|
if self._is_standby or self._program is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._program.music_artist
|
||||||
|
|
||||||
|
@property
|
||||||
|
def media_album_name(self):
|
||||||
|
"""Album name of current playing media, music track only."""
|
||||||
|
if self._is_standby or self._program is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._program.music_album
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_series_title(self):
|
def media_series_title(self):
|
||||||
"""Return the title of current episode of TV show."""
|
"""Return the title of current episode of TV show."""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Tests for the DirecTV component."""
|
"""Tests for the DirecTV component."""
|
||||||
from homeassistant.components.directv.const import CONF_RECEIVER_ID, DOMAIN
|
from homeassistant.components.directv.const import CONF_RECEIVER_ID, DOMAIN
|
||||||
from homeassistant.components.ssdp import ATTR_SSDP_LOCATION
|
from homeassistant.components.ssdp import ATTR_SSDP_LOCATION
|
||||||
from homeassistant.const import CONF_HOST, HTTP_INTERNAL_SERVER_ERROR
|
from homeassistant.const import CONF_HOST, HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import MockConfigEntry, load_fixture
|
||||||
@ -31,6 +31,13 @@ def mock_connection(aioclient_mock: AiohttpClientMocker) -> None:
|
|||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
aioclient_mock.get(
|
||||||
|
f"http://{HOST}:8080/info/mode",
|
||||||
|
params={"clientAddr": "B01234567890"},
|
||||||
|
text=load_fixture("directv/info-mode-standby.json"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
|
||||||
aioclient_mock.get(
|
aioclient_mock.get(
|
||||||
f"http://{HOST}:8080/info/mode",
|
f"http://{HOST}:8080/info/mode",
|
||||||
params={"clientAddr": "9XXXXXXXXXX9"},
|
params={"clientAddr": "9XXXXXXXXXX9"},
|
||||||
@ -64,6 +71,21 @@ def mock_connection(aioclient_mock: AiohttpClientMocker) -> None:
|
|||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
aioclient_mock.get(
|
||||||
|
f"http://{HOST}:8080/tv/getTuned",
|
||||||
|
params={"clientAddr": "A01234567890"},
|
||||||
|
text=load_fixture("directv/tv-get-tuned-music.json"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
|
||||||
|
aioclient_mock.get(
|
||||||
|
f"http://{HOST}:8080/tv/getTuned",
|
||||||
|
params={"clientAddr": "C01234567890"},
|
||||||
|
status=HTTP_FORBIDDEN,
|
||||||
|
text=load_fixture("directv/tv-get-tuned-restricted.json"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
|
||||||
aioclient_mock.get(
|
aioclient_mock.get(
|
||||||
f"http://{HOST}:8080/tv/getTuned",
|
f"http://{HOST}:8080/tv/getTuned",
|
||||||
text=load_fixture("directv/tv-get-tuned-movie.json"),
|
text=load_fixture("directv/tv-get-tuned-movie.json"),
|
||||||
|
@ -13,6 +13,8 @@ from homeassistant.components.directv.media_player import (
|
|||||||
)
|
)
|
||||||
from homeassistant.components.media_player.const import (
|
from homeassistant.components.media_player.const import (
|
||||||
ATTR_INPUT_SOURCE,
|
ATTR_INPUT_SOURCE,
|
||||||
|
ATTR_MEDIA_ALBUM_NAME,
|
||||||
|
ATTR_MEDIA_ARTIST,
|
||||||
ATTR_MEDIA_CHANNEL,
|
ATTR_MEDIA_CHANNEL,
|
||||||
ATTR_MEDIA_CONTENT_ID,
|
ATTR_MEDIA_CONTENT_ID,
|
||||||
ATTR_MEDIA_CONTENT_TYPE,
|
ATTR_MEDIA_CONTENT_TYPE,
|
||||||
@ -24,6 +26,7 @@ from homeassistant.components.media_player.const import (
|
|||||||
ATTR_MEDIA_TITLE,
|
ATTR_MEDIA_TITLE,
|
||||||
DOMAIN as MP_DOMAIN,
|
DOMAIN as MP_DOMAIN,
|
||||||
MEDIA_TYPE_MOVIE,
|
MEDIA_TYPE_MOVIE,
|
||||||
|
MEDIA_TYPE_MUSIC,
|
||||||
MEDIA_TYPE_TVSHOW,
|
MEDIA_TYPE_TVSHOW,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
SUPPORT_NEXT_TRACK,
|
SUPPORT_NEXT_TRACK,
|
||||||
@ -44,6 +47,7 @@ from homeassistant.const import (
|
|||||||
SERVICE_MEDIA_STOP,
|
SERVICE_MEDIA_STOP,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
|
STATE_OFF,
|
||||||
STATE_PAUSED,
|
STATE_PAUSED,
|
||||||
STATE_PLAYING,
|
STATE_PLAYING,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
@ -57,6 +61,9 @@ from tests.test_util.aiohttp import AiohttpClientMocker
|
|||||||
ATTR_UNIQUE_ID = "unique_id"
|
ATTR_UNIQUE_ID = "unique_id"
|
||||||
CLIENT_ENTITY_ID = f"{MP_DOMAIN}.client"
|
CLIENT_ENTITY_ID = f"{MP_DOMAIN}.client"
|
||||||
MAIN_ENTITY_ID = f"{MP_DOMAIN}.host"
|
MAIN_ENTITY_ID = f"{MP_DOMAIN}.host"
|
||||||
|
MUSIC_ENTITY_ID = f"{MP_DOMAIN}.music_client"
|
||||||
|
RESTRICTED_ENTITY_ID = f"{MP_DOMAIN}.restricted_client"
|
||||||
|
STANDBY_ENTITY_ID = f"{MP_DOMAIN}.standby_client"
|
||||||
UNAVAILABLE_ENTITY_ID = f"{MP_DOMAIN}.unavailable_client"
|
UNAVAILABLE_ENTITY_ID = f"{MP_DOMAIN}.unavailable_client"
|
||||||
|
|
||||||
# pylint: disable=redefined-outer-name
|
# pylint: disable=redefined-outer-name
|
||||||
@ -250,6 +257,63 @@ async def test_check_attributes(
|
|||||||
2010, 7, 5, 15, 0, 8, tzinfo=dt_util.UTC
|
2010, 7, 5, 15, 0, 8, tzinfo=dt_util.UTC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(MUSIC_ENTITY_ID)
|
||||||
|
assert state.state == STATE_PLAYING
|
||||||
|
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_ID) == "76917562"
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_TYPE) == MEDIA_TYPE_MUSIC
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_DURATION) == 86400
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION) == 15050
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION_UPDATED_AT)
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_TITLE) == "Sparkle In Your Eyes"
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ARTIST) == "Gerald Albright"
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ALBUM_NAME) == "Slam Dunk (2014)"
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_SERIES_TITLE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CHANNEL) == "{} ({})".format("MCSJ", "851")
|
||||||
|
assert state.attributes.get(ATTR_INPUT_SOURCE) == "851"
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING)
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_RATING) == "TV-PG"
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_RECORDED)
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_START_TIME) == datetime(
|
||||||
|
2020, 3, 21, 10, 0, 0, tzinfo=dt_util.UTC
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(STANDBY_ENTITY_ID)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_ID) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_TYPE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_DURATION) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION_UPDATED_AT) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_TITLE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ARTIST) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ALBUM_NAME) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_SERIES_TITLE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CHANNEL) is None
|
||||||
|
assert state.attributes.get(ATTR_INPUT_SOURCE) is None
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING)
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_RATING) is None
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_RECORDED)
|
||||||
|
|
||||||
|
state = hass.states.get(RESTRICTED_ENTITY_ID)
|
||||||
|
assert state.state == STATE_PLAYING
|
||||||
|
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_ID) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CONTENT_TYPE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_DURATION) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_POSITION_UPDATED_AT) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_TITLE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ARTIST) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_ALBUM_NAME) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_SERIES_TITLE) is None
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_CHANNEL) is None
|
||||||
|
assert state.attributes.get(ATTR_INPUT_SOURCE) is None
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING)
|
||||||
|
assert state.attributes.get(ATTR_MEDIA_RATING) is None
|
||||||
|
assert not state.attributes.get(ATTR_MEDIA_RECORDED)
|
||||||
|
|
||||||
state = hass.states.get(UNAVAILABLE_ENTITY_ID)
|
state = hass.states.get(UNAVAILABLE_ENTITY_ID)
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
12
tests/fixtures/directv/info-get-locations.json
vendored
12
tests/fixtures/directv/info-get-locations.json
vendored
@ -8,6 +8,18 @@
|
|||||||
"clientAddr": "2CA17D1CD30X",
|
"clientAddr": "2CA17D1CD30X",
|
||||||
"locationName": "Client"
|
"locationName": "Client"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"clientAddr": "A01234567890",
|
||||||
|
"locationName": "Music Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientAddr": "B01234567890",
|
||||||
|
"locationName": "Standby Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientAddr": "C01234567890",
|
||||||
|
"locationName": "Restricted Client"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"clientAddr": "9XXXXXXXXXX9",
|
"clientAddr": "9XXXXXXXXXX9",
|
||||||
"locationName": "Unavailable Client"
|
"locationName": "Unavailable Client"
|
||||||
|
9
tests/fixtures/directv/info-mode-standby.json
vendored
Normal file
9
tests/fixtures/directv/info-mode-standby.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"mode": 1,
|
||||||
|
"status": {
|
||||||
|
"code": 200,
|
||||||
|
"commandResult": 0,
|
||||||
|
"msg": "OK",
|
||||||
|
"query": "/info/mode"
|
||||||
|
}
|
||||||
|
}
|
28
tests/fixtures/directv/tv-get-tuned-music.json
vendored
Normal file
28
tests/fixtures/directv/tv-get-tuned-music.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"callsign": "MCSJ",
|
||||||
|
"duration": 86400,
|
||||||
|
"isOffAir": false,
|
||||||
|
"isPclocked": 3,
|
||||||
|
"isPpv": false,
|
||||||
|
"isRecording": false,
|
||||||
|
"isVod": false,
|
||||||
|
"major": 851,
|
||||||
|
"minor": 65535,
|
||||||
|
"music": {
|
||||||
|
"by": "Gerald Albright",
|
||||||
|
"cd": "Slam Dunk (2014)",
|
||||||
|
"title": "Sparkle In Your Eyes"
|
||||||
|
},
|
||||||
|
"offset": 15050,
|
||||||
|
"programId": "76917562",
|
||||||
|
"rating": "TV-PG",
|
||||||
|
"startTime": 1584784800,
|
||||||
|
"stationId": 2872196,
|
||||||
|
"status": {
|
||||||
|
"code": 200,
|
||||||
|
"commandResult": 0,
|
||||||
|
"msg": "OK.",
|
||||||
|
"query": "/tv/getTuned"
|
||||||
|
},
|
||||||
|
"title": "Smooth Jazz"
|
||||||
|
}
|
8
tests/fixtures/directv/tv-get-tuned-restricted.json
vendored
Normal file
8
tests/fixtures/directv/tv-get-tuned-restricted.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"status": {
|
||||||
|
"code": 403,
|
||||||
|
"commandResult": 1,
|
||||||
|
"msg": "Forbidden.",
|
||||||
|
"query": "/tv/getTuned"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user