"""Helpers for Roku.""" from __future__ import annotations from collections.abc import Awaitable, Callable, Coroutine from functools import wraps import logging from typing import Any, TypeVar from rokuecp import RokuConnectionError, RokuConnectionTimeoutError, RokuError from typing_extensions import Concatenate, ParamSpec from .entity import RokuEntity _LOGGER = logging.getLogger(__name__) _RokuEntityT = TypeVar("_RokuEntityT", bound=RokuEntity) _P = ParamSpec("_P") def format_channel_name(channel_number: str, channel_name: str | None = None) -> str: """Format a Roku Channel name.""" if channel_name is not None and channel_name != "": return f"{channel_name} ({channel_number})" return channel_number def roku_exception_handler( ignore_timeout: bool = False, ) -> Callable[ [Callable[Concatenate[_RokuEntityT, _P], Awaitable[Any]]], Callable[Concatenate[_RokuEntityT, _P], Coroutine[Any, Any, None]], ]: """Decorate Roku calls to handle Roku exceptions.""" def decorator( func: Callable[Concatenate[_RokuEntityT, _P], Awaitable[Any]], ) -> Callable[Concatenate[_RokuEntityT, _P], Coroutine[Any, Any, None]]: @wraps(func) async def wrapper( self: _RokuEntityT, *args: _P.args, **kwargs: _P.kwargs ) -> None: try: await func(self, *args, **kwargs) except RokuConnectionTimeoutError as error: if not ignore_timeout and self.available: _LOGGER.error("Error communicating with API: %s", error) except RokuConnectionError as error: if self.available: _LOGGER.error("Error communicating with API: %s", error) except RokuError as error: if self.available: _LOGGER.error("Invalid response from API: %s", error) return wrapper return decorator