Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch API errors in cast media_player service handlers #113839

Merged
merged 3 commits into from
Mar 20, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion homeassistant/components/cast/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from collections.abc import Callable
from contextlib import suppress
from datetime import datetime
from functools import wraps
import json
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar

import pychromecast
from pychromecast.controllers.homeassistant import HomeAssistantController
Expand All @@ -19,6 +20,7 @@
)
from pychromecast.controllers.multizone import MultizoneManager
from pychromecast.controllers.receiver import VOLUME_CONTROL_TYPE_FIXED
from pychromecast.error import PyChromecastError
from pychromecast.quick_play import quick_play
from pychromecast.socket_client import (
CONNECTION_STATUS_CONNECTED,
Expand Down Expand Up @@ -84,6 +86,34 @@
CAST_SPLASH = "https://www.home-assistant.io/images/cast/splash.png"


_CastDeviceT = TypeVar("_CastDeviceT", bound="CastDevice")
_R = TypeVar("_R")
_P = ParamSpec("_P")

_FuncType = Callable[Concatenate[_CastDeviceT, _P], _R]
_ReturnFuncType = Callable[Concatenate[_CastDeviceT, _P], _R]


def api_error(
func: _FuncType[_CastDeviceT, _P, _R],
) -> _ReturnFuncType[_CastDeviceT, _P, _R]:
"""Handle PyChromecastError and reraise a HomeAssistantError."""

@wraps(func)
def wrapper(self: _CastDeviceT, *args: _P.args, **kwargs: _P.kwargs) -> _R:
"""Wrap a CastDevice method."""
try:
return_value = func(self, *args, **kwargs)
except PyChromecastError as err:
raise HomeAssistantError(

Check warning on line 108 in homeassistant/components/cast/media_player.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/cast/media_player.py#L107-L108

Added lines #L107 - L108 were not covered by tests
f"{self.__class__.__name__}.{func.__name__} Failed: {err}"
) from err

return return_value

return wrapper


@callback
def _async_create_cast_device(hass: HomeAssistant, info: ChromecastInfo):
"""Create a CastDevice entity or dynamic group from the chromecast object.
Expand Down Expand Up @@ -478,6 +508,7 @@

return media_controller

@api_error
def turn_on(self) -> None:
"""Turn on the cast device."""

Expand All @@ -497,43 +528,52 @@
else:
chromecast.start_app(pychromecast.config.APP_MEDIA_RECEIVER)

@api_error
def turn_off(self) -> None:
"""Turn off the cast device."""
self._get_chromecast().quit_app()

@api_error
def mute_volume(self, mute: bool) -> None:
"""Mute the volume."""
self._get_chromecast().set_volume_muted(mute)

@api_error
def set_volume_level(self, volume: float) -> None:
"""Set volume level, range 0..1."""
self._get_chromecast().set_volume(volume)

@api_error
def media_play(self) -> None:
"""Send play command."""
media_controller = self._media_controller()
media_controller.play()

@api_error
def media_pause(self) -> None:
"""Send pause command."""
media_controller = self._media_controller()
media_controller.pause()

@api_error
def media_stop(self) -> None:
"""Send stop command."""
media_controller = self._media_controller()
media_controller.stop()

@api_error
def media_previous_track(self) -> None:
"""Send previous track command."""
media_controller = self._media_controller()
media_controller.queue_prev()

@api_error
def media_next_track(self) -> None:
"""Send next track command."""
media_controller = self._media_controller()
media_controller.queue_next()

@api_error
def media_seek(self, position: float) -> None:
"""Seek the media to a specific location."""
media_controller = self._media_controller()
Expand Down Expand Up @@ -616,6 +656,7 @@
self.hass, media_content_id, content_filter=content_filter
)

@api_error
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
async def async_play_media(
self, media_type: MediaType | str, media_id: str, **kwargs: Any
) -> None:
Expand Down
Loading