1 """Class representing Sonos favorites."""
3 from __future__
import annotations
5 from collections.abc
import Iterator
8 from typing
import TYPE_CHECKING, Any
11 from soco.data_structures
import DidlFavorite
12 from soco.events_base
import Event
as SonosEvent
13 from soco.exceptions
import SoCoException
17 from .const
import SONOS_CREATE_FAVORITES_SENSOR, SONOS_FAVORITES_UPDATED
18 from .helpers
import soco_error
19 from .household_coordinator
import SonosHouseholdCoordinator
22 from .speaker
import SonosSpeaker
24 _LOGGER = logging.getLogger(__name__)
28 """Coordinator class for Sonos favorites."""
31 """Initialize the data."""
33 self.
_favorites_favorites: list[DidlFavorite] = []
34 self.last_polled_ids: dict[str, int] = {}
37 """Return an iterator for the known favorites."""
39 return iter(favorites)
41 def setup(self, soco: SoCo) ->
None:
42 """Override to send a signal on base class setup completion."""
48 """Return the number of favorites."""
52 """Return the favorite object with the provided item_id."""
53 return next((fav
for fav
in self.
_favorites_favorites
if fav.item_id == item_id),
None)
56 self, soco: SoCo, update_id: int |
None =
None
58 """Update the cache and update entities."""
59 updated = await self.
hasshass.async_add_executor_job(
66 self.
hasshass, f
"{SONOS_FAVORITES_UPDATED}-{self.household_id}"
70 self, event: SonosEvent, speaker: SonosSpeaker
72 """Process the event payload in an async lock and update entities."""
73 event_id = event.variables[
"favorites_update_id"]
74 container_ids = event.variables[
"container_update_i_ds"]
75 if not (match := re.search(
r"FV:2,(\d+)", container_ids)):
78 container_id =
int(match.groups()[0])
79 event_id =
int(event_id.split(
",")[-1])
82 last_poll_id = self.last_polled_ids.
get(speaker.uid)
89 self.last_polled_ids[speaker.uid] = container_id
92 if last_poll_id
and container_id <= last_poll_id:
95 speaker.event_stats.process(event)
97 "New favorites event %s from %s (was %s)",
106 def update_cache(self, soco: SoCo, update_id: int |
None =
None) -> bool:
107 """Update cache of known favorites and return if cache has changed."""
108 new_favorites = soco.music_library.get_sonos_favorites()
112 last_poll_id = self.last_polled_ids.
get(soco.uid)
113 if last_poll_id
and new_favorites.update_id <= last_poll_id:
116 self.last_polled_ids[soco.uid] = new_favorites.update_id
119 "Processing favorites update_id %s for %s (was: %s)",
120 new_favorites.update_id,
126 for fav
in new_favorites:
129 if fav.reference.resources:
131 except SoCoException
as ex:
133 _LOGGER.error(
"Unhandled favorite '%s': %s", fav.title, ex)
136 "Cached %s favorites for household %s using %s",
Iterator[DidlFavorite] __iter__(self)
None __init__(self, *Any args)
None async_process_event(self, SonosEvent event, SonosSpeaker speaker)
None setup(self, SoCo soco)
None async_update_entities(self, SoCo soco, int|None update_id=None)
DidlFavorite|None lookup_by_item_id(self, str item_id)
bool update_cache(self, SoCo soco, int|None update_id=None)
None async_update_entities(self, SoCo soco, int|None update_id=None)
bool update_cache(self, SoCo soco, int|None update_id=None)
web.Response get(self, web.Request request, str config_key)
None dispatcher_send(HomeAssistant hass, str signal, *Any args)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)