from typing import List, Optional
import pychromecast
+from asyncio_throttle import Throttler
from music_assistant.helpers.typing import MusicAssistantType
from music_assistant.helpers.util import async_yield_chunks, compare_strings
from music_assistant.models.config_entry import ConfigEntry
self._available = False
self._status_listener: Optional[CastStatusListener] = None
self._is_speaker_group = False
+ self._throttler = Throttler(rate_limit=2, period=1)
@property
def player_id(self) -> str:
if self._chromecast and not self._available:
try:
self.disconnect()
- except Exception as exc: # pylint: disable=broad-except
- LOGGER.exception(exc)
+ except Exception: # pylint: disable=broad-except
+ pass
elif self._chromecast is not None:
return
LOGGER.debug(
self._available = new_available
self.update_state()
if self._cast_info.is_audio_group and new_available:
- self.try_chromecast_command(
+ self.__try_chromecast_command(
self._chromecast.mz_controller.update_members
)
"group_player"
):
# the group player wants very accurate elapsed_time state so we request it very often
- self.try_chromecast_command(self._chromecast.media_controller.update_status)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.update_status
+ )
self.update_state()
# ========== Service Calls ==========
async def async_cmd_stop(self) -> None:
"""Send stop command to player."""
if self._chromecast and self._chromecast.media_controller:
- self.try_chromecast_command(self._chromecast.media_controller.stop)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.stop
+ )
async def async_cmd_play(self) -> None:
"""Send play command to player."""
if self._chromecast.media_controller:
- self.try_chromecast_command(self._chromecast.media_controller.play)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.play
+ )
async def async_cmd_pause(self) -> None:
"""Send pause command to player."""
if self._chromecast.media_controller:
- self.try_chromecast_command(self._chromecast.media_controller.pause)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.pause
+ )
async def async_cmd_next(self) -> None:
"""Send next track command to player."""
if self._chromecast.media_controller:
- self.try_chromecast_command(self._chromecast.media_controller.queue_next)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.queue_next
+ )
async def async_cmd_previous(self) -> None:
"""Send previous track command to player."""
if self._chromecast.media_controller:
- self.try_chromecast_command(self._chromecast.media_controller.queue_prev)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.queue_prev
+ )
async def async_cmd_power_on(self) -> None:
"""Send power ON command to player."""
- self.try_chromecast_command(self._chromecast.set_volume_muted, False)
+ await self.__async_try_chromecast_command(
+ self._chromecast.set_volume_muted, False
+ )
async def async_cmd_power_off(self) -> None:
"""Send power OFF command to player."""
or self.media_status.player_is_paused
or self.media_status.player_is_idle
):
- self.try_chromecast_command(self._chromecast.media_controller.stop)
+ await self.__async_try_chromecast_command(
+ self._chromecast.media_controller.stop
+ )
# chromecast has no real poweroff so we send mute instead
- self.try_chromecast_command(self._chromecast.set_volume_muted, True)
+ await self.__async_try_chromecast_command(
+ self._chromecast.set_volume_muted, True
+ )
async def async_cmd_volume_set(self, volume_level: int) -> None:
"""Send new volume level command to player."""
- self.try_chromecast_command(self._chromecast.set_volume, volume_level / 100)
+ await self.__async_try_chromecast_command(
+ self._chromecast.set_volume, volume_level / 100
+ )
async def async_cmd_volume_mute(self, is_muted: bool = False) -> None:
"""Send mute command to player."""
- self.try_chromecast_command(self._chromecast.set_volume_muted, is_muted)
+ await self.__async_try_chromecast_command(
+ self._chromecast.set_volume_muted, is_muted
+ )
async def async_cmd_play_uri(self, uri: str) -> None:
"""Play single uri on player."""
queue_item.name = "Music Assistant"
queue_item.uri = uri
return await self.async_cmd_queue_load([queue_item, queue_item])
- self.try_chromecast_command(self._chromecast.play_media, uri, "audio/flac")
+ await self.__async_try_chromecast_command(
+ self._chromecast.play_media, uri, "audio/flac"
+ )
async def async_cmd_queue_load(self, queue_items: List[QueueItem]) -> None:
"""Load (overwrite) queue with new items."""
"startIndex": 0, # Item index to play after this request or keep same item if undefined
"items": cc_queue_items, # only load 50 tracks at once or the socket will crash
}
- self.try_chromecast_command(self.__send_player_queue, queuedata)
+ await self.__async_try_chromecast_command(self.__send_player_queue, queuedata)
if len(queue_items) > 50:
await self.async_cmd_queue_append(queue_items[51:])
"insertBefore": None,
"items": chunk,
}
- self.try_chromecast_command(self.__send_player_queue, queuedata)
+ await self.__async_try_chromecast_command(
+ self.__send_player_queue, queuedata
+ )
def __create_queue_items(self, tracks) -> None:
"""Create list of CC queue items from tracks."""
else:
send_queue()
- def try_chromecast_command(self, func, *args, **kwargs):
+ async def __try_chromecast_command(self, func, *args, **kwargs):
+ """Try to execute Chromecast command."""
+ self.mass.add_job(self.__async_try_chromecast_command(func, *args, **kwargs))
+
+ async def __async_try_chromecast_command(self, func, *args, **kwargs):
"""Try to execute Chromecast command."""
def handle_command(func, *args, **kwarg):
or not self._available
):
LOGGER.error(
- "Error while executing command on player %s: Chromecast is not available!"
+ "Error while executing command %s on player %s: Chromecast is not available!",
+ func.__name__,
+ self.name,
)
+ return
try:
return func(*args, **kwargs)
except (
pychromecast.NotConnected,
pychromecast.ChromecastConnectionError,
) as exc:
- LOGGER.error(
- "Error while executing command on player %s: %s",
+ LOGGER.warning(
+ "Error while executing command %s on player %s: %s",
+ func.__name__,
self.name,
str(exc),
)
except Exception as exc: # pylint: disable=broad-except
LOGGER.exception(exc)
- self.mass.add_job(handle_command, func, *args, **kwargs)
+ async with self._throttler:
+ self.mass.add_job(handle_command, func, *args, **kwargs)