mirror of https://github.com/home-assistant/core
209 lines
5.7 KiB
Python
209 lines
5.7 KiB
Python
"""Home Assistant Cast platform."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pychromecast import Chromecast
|
|
from pychromecast.const import CAST_TYPE_CHROMECAST
|
|
|
|
from homeassistant.components.cast import DOMAIN as CAST_DOMAIN
|
|
from homeassistant.components.cast.home_assistant_cast import (
|
|
ATTR_URL_PATH,
|
|
ATTR_VIEW_PATH,
|
|
NO_URL_AVAILABLE_ERROR,
|
|
SERVICE_SHOW_VIEW,
|
|
)
|
|
from homeassistant.components.media_player import (
|
|
BrowseError,
|
|
BrowseMedia,
|
|
MediaClass,
|
|
MediaType,
|
|
)
|
|
from homeassistant.const import ATTR_ENTITY_ID
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
|
|
|
from .const import DOMAIN, ConfigNotFound
|
|
from .dashboard import LovelaceConfig
|
|
|
|
DEFAULT_DASHBOARD = "_default_"
|
|
|
|
|
|
async def async_get_media_browser_root_object(
|
|
hass: HomeAssistant, cast_type: str
|
|
) -> list[BrowseMedia]:
|
|
"""Create a root object for media browsing."""
|
|
if cast_type != CAST_TYPE_CHROMECAST:
|
|
return []
|
|
return [
|
|
BrowseMedia(
|
|
title="Dashboards",
|
|
media_class=MediaClass.APP,
|
|
media_content_id="",
|
|
media_content_type=DOMAIN,
|
|
thumbnail="https://brands.home-assistant.io/_/lovelace/logo.png",
|
|
can_play=False,
|
|
can_expand=True,
|
|
)
|
|
]
|
|
|
|
|
|
async def async_browse_media(
|
|
hass: HomeAssistant,
|
|
media_content_type: MediaType | str,
|
|
media_content_id: str,
|
|
cast_type: str,
|
|
) -> BrowseMedia | None:
|
|
"""Browse media."""
|
|
if media_content_type != DOMAIN:
|
|
return None
|
|
|
|
try:
|
|
get_url(hass, require_ssl=True, prefer_external=True)
|
|
except NoURLAvailableError as err:
|
|
raise BrowseError(NO_URL_AVAILABLE_ERROR) from err
|
|
|
|
# List dashboards.
|
|
if not media_content_id:
|
|
children = [
|
|
BrowseMedia(
|
|
title="Default",
|
|
media_class=MediaClass.APP,
|
|
media_content_id=DEFAULT_DASHBOARD,
|
|
media_content_type=DOMAIN,
|
|
thumbnail="https://brands.home-assistant.io/_/lovelace/logo.png",
|
|
can_play=True,
|
|
can_expand=False,
|
|
)
|
|
]
|
|
for url_path in hass.data[DOMAIN]["dashboards"]:
|
|
if url_path is None:
|
|
continue
|
|
|
|
info = await _get_dashboard_info(hass, url_path)
|
|
children.append(_item_from_info(info))
|
|
|
|
root = (await async_get_media_browser_root_object(hass, CAST_TYPE_CHROMECAST))[
|
|
0
|
|
]
|
|
root.children = children
|
|
return root
|
|
|
|
try:
|
|
info = await _get_dashboard_info(hass, media_content_id)
|
|
except ValueError as err:
|
|
raise BrowseError(f"Dashboard {media_content_id} not found") from err
|
|
|
|
children = []
|
|
|
|
for view in info["views"]:
|
|
children.append(
|
|
BrowseMedia(
|
|
title=view["title"],
|
|
media_class=MediaClass.APP,
|
|
media_content_id=f'{info["url_path"]}/{view["path"]}',
|
|
media_content_type=DOMAIN,
|
|
thumbnail="https://brands.home-assistant.io/_/lovelace/logo.png",
|
|
can_play=True,
|
|
can_expand=False,
|
|
)
|
|
)
|
|
|
|
root = _item_from_info(info)
|
|
root.children = children
|
|
return root
|
|
|
|
|
|
async def async_play_media(
|
|
hass: HomeAssistant,
|
|
cast_entity_id: str,
|
|
chromecast: Chromecast,
|
|
media_type: MediaType | str,
|
|
media_id: str,
|
|
) -> bool:
|
|
"""Play media."""
|
|
if media_type != DOMAIN:
|
|
return False
|
|
|
|
if "/" in media_id:
|
|
url_path, view_path = media_id.split("/", 1)
|
|
else:
|
|
url_path = media_id
|
|
try:
|
|
info = await _get_dashboard_info(hass, media_id)
|
|
except ValueError as err:
|
|
raise HomeAssistantError(f"Invalid dashboard {media_id} specified") from err
|
|
view_path = info["views"][0]["path"] if info["views"] else "0"
|
|
|
|
data = {
|
|
ATTR_ENTITY_ID: cast_entity_id,
|
|
ATTR_VIEW_PATH: view_path,
|
|
}
|
|
if url_path != DEFAULT_DASHBOARD:
|
|
data[ATTR_URL_PATH] = url_path
|
|
|
|
await hass.services.async_call(
|
|
CAST_DOMAIN,
|
|
SERVICE_SHOW_VIEW,
|
|
data,
|
|
blocking=True,
|
|
)
|
|
return True
|
|
|
|
|
|
async def _get_dashboard_info(hass, url_path):
|
|
"""Load a dashboard and return info on views."""
|
|
if url_path == DEFAULT_DASHBOARD:
|
|
url_path = None
|
|
dashboard: LovelaceConfig | None = hass.data[DOMAIN]["dashboards"].get(url_path)
|
|
|
|
if dashboard is None:
|
|
raise ValueError("Invalid dashboard specified")
|
|
|
|
try:
|
|
config = await dashboard.async_load(False)
|
|
except ConfigNotFound:
|
|
config = None
|
|
|
|
if dashboard.url_path is None:
|
|
url_path = DEFAULT_DASHBOARD
|
|
title = "Default"
|
|
else:
|
|
url_path = dashboard.url_path
|
|
title = config.get("title", url_path) if config else url_path
|
|
|
|
views = []
|
|
data = {
|
|
"title": title,
|
|
"url_path": url_path,
|
|
"views": views,
|
|
}
|
|
|
|
if config is None or "views" not in config:
|
|
return data
|
|
|
|
for idx, view in enumerate(config["views"]):
|
|
path = view.get("path", f"{idx}")
|
|
views.append(
|
|
{
|
|
"title": view.get("title", path),
|
|
"path": path,
|
|
}
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
@callback
|
|
def _item_from_info(info: dict) -> BrowseMedia:
|
|
"""Convert dashboard info to browse item."""
|
|
return BrowseMedia(
|
|
title=info["title"],
|
|
media_class=MediaClass.APP,
|
|
media_content_id=info["url_path"],
|
|
media_content_type=DOMAIN,
|
|
thumbnail="https://brands.home-assistant.io/_/lovelace/logo.png",
|
|
can_play=True,
|
|
can_expand=len(info["views"]) > 1,
|
|
)
|