1
mirror of https://github.com/home-assistant/core synced 2024-09-03 08:14:07 +02:00
ha-core/homeassistant/components/sonos/household_coordinator.py
jjlawren a7fd477c64
Refactor Sonos polling (#65722)
* Refactor Sonos polling

Explicitly rename fallback polling
Catch soco exceptions centrally where possible
Create SonosPollingEntity subclass
Remove unnecessary soco_error fixture argument
Remove unnecessary polling in update_volume()
Adjust log levels and wording
Set explicit timeout on library

* Adjust logging to use raised exceptions

* Simplify availabiliity checks when using built-in poller

* Fix typing for return values
2022-02-08 12:17:05 -06:00

79 lines
2.5 KiB
Python

"""Class representing a Sonos household storage helper."""
from __future__ import annotations
import asyncio
from collections.abc import Callable, Coroutine
import logging
from soco import SoCo
from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer
from .const import DATA_SONOS
from .exception import SonosUpdateError
_LOGGER = logging.getLogger(__name__)
class SonosHouseholdCoordinator:
"""Base class for Sonos household-level storage."""
def __init__(self, hass: HomeAssistant, household_id: str) -> None:
"""Initialize the data."""
self.hass = hass
self.household_id = household_id
self.async_poll: Callable[[], Coroutine[None, None, None]] | None = None
self.last_processed_event_id: int | None = None
self.cache_update_lock: asyncio.Lock | None = None
def setup(self, soco: SoCo) -> None:
"""Set up the SonosAlarm instance."""
self.update_cache(soco)
self.hass.add_job(self._async_setup)
async def _async_setup(self) -> None:
"""Finish setup in async context."""
self.cache_update_lock = asyncio.Lock()
self.async_poll = Debouncer(
self.hass,
_LOGGER,
cooldown=3,
immediate=False,
function=self._async_poll,
).async_call
@property
def class_type(self) -> str:
"""Return the class type of this instance."""
return type(self).__name__
async def _async_poll(self) -> None:
"""Poll any known speaker."""
discovered = self.hass.data[DATA_SONOS].discovered
for uid, speaker in discovered.items():
_LOGGER.debug("Polling %s using %s", self.class_type, speaker.soco)
try:
await self.async_update_entities(speaker.soco)
except SonosUpdateError as err:
_LOGGER.error(
"Could not refresh %s: %s",
self.class_type,
err,
)
else:
# Prefer this SoCo instance next update
discovered.move_to_end(uid, last=False)
break
async def async_update_entities(
self, soco: SoCo, update_id: int | None = None
) -> None:
"""Update the cache and update entities."""
raise NotImplementedError()
def update_cache(self, soco: SoCo, update_id: int | None = None) -> bool:
"""Update the cache of the household-level feature and return if cache has changed."""
raise NotImplementedError()