diff --git a/homeassistant/components/sonos/binary_sensor.py b/homeassistant/components/sonos/binary_sensor.py index 21e0c077136..8583132521c 100644 --- a/homeassistant/components/sonos/binary_sensor.py +++ b/homeassistant/components/sonos/binary_sensor.py @@ -1,7 +1,6 @@ """Entity representing a Sonos power sensor.""" from __future__ import annotations -import datetime import logging from typing import Any @@ -50,7 +49,7 @@ class SonosPowerEntity(SonosEntity, BinarySensorEntity): """Return the entity's device class.""" return DEVICE_CLASS_BATTERY_CHARGING - async def async_update(self, now: datetime.datetime | None = None) -> None: + async def async_update(self) -> None: """Poll the device for the current state.""" await self.speaker.async_poll_battery() diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index c32f981e345..0a70844e6b5 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -136,7 +136,7 @@ SONOS_CREATE_ALARM = "sonos_create_alarm" SONOS_CREATE_BATTERY = "sonos_create_battery" SONOS_CREATE_MEDIA_PLAYER = "sonos_create_media_player" SONOS_ENTITY_CREATED = "sonos_entity_created" -SONOS_ENTITY_UPDATE = "sonos_entity_update" +SONOS_POLL_UPDATE = "sonos_poll_update" SONOS_GROUP_UPDATE = "sonos_group_update" SONOS_HOUSEHOLD_UPDATED = "sonos_household_updated" SONOS_ALARM_UPDATE = "sonos_alarm_update" diff --git a/homeassistant/components/sonos/entity.py b/homeassistant/components/sonos/entity.py index 8632357d618..8c47c69b2d7 100644 --- a/homeassistant/components/sonos/entity.py +++ b/homeassistant/components/sonos/entity.py @@ -1,6 +1,7 @@ """Entity representing a Sonos player.""" from __future__ import annotations +import datetime import logging from pysonos.core import SoCo @@ -15,8 +16,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from .const import ( DOMAIN, SONOS_ENTITY_CREATED, - SONOS_ENTITY_UPDATE, SONOS_HOUSEHOLD_UPDATED, + SONOS_POLL_UPDATE, SONOS_STATE_UPDATED, ) from .speaker import SonosSpeaker @@ -38,8 +39,8 @@ class SonosEntity(Entity): self.async_on_remove( async_dispatcher_connect( self.hass, - f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}", - self.async_update, # pylint: disable=no-member + f"{SONOS_POLL_UPDATE}-{self.soco.uid}", + self.async_poll, ) ) self.async_on_remove( @@ -60,6 +61,17 @@ class SonosEntity(Entity): self.hass, f"{SONOS_ENTITY_CREATED}-{self.soco.uid}", self.platform.domain ) + async def async_poll(self, now: datetime.datetime) -> None: + """Poll the entity if subscriptions fail.""" + if self.speaker.is_first_poll: + _LOGGER.warning( + "%s cannot reach [%s], falling back to polling, functionality may be limited", + self.speaker.zone_name, + self.speaker.subscription_address, + ) + self.speaker.is_first_poll = False + await self.async_update() # pylint: disable=no-member + @property def soco(self) -> SoCo: """Return the speaker SoCo instance.""" diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 0200ae11aa8..133fe9efe8f 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -293,13 +293,12 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): return STATE_PLAYING return STATE_IDLE - async def async_update(self, now: datetime.datetime | None = None) -> None: + async def async_update(self) -> None: """Retrieve latest state.""" - await self.hass.async_add_executor_job(self._update, now) + await self.hass.async_add_executor_job(self._update) - def _update(self, now: datetime.datetime | None = None) -> None: + def _update(self) -> None: """Retrieve latest state.""" - _LOGGER.debug("Polling speaker %s", self.speaker.zone_name) try: self.speaker.update_groups() self.speaker.update_volume() diff --git a/homeassistant/components/sonos/sensor.py b/homeassistant/components/sonos/sensor.py index d9ff19af581..9e5277819a7 100644 --- a/homeassistant/components/sonos/sensor.py +++ b/homeassistant/components/sonos/sensor.py @@ -1,7 +1,6 @@ """Entity representing a Sonos battery level.""" from __future__ import annotations -import datetime import logging from homeassistant.components.sensor import SensorEntity @@ -50,7 +49,7 @@ class SonosBatteryEntity(SonosEntity, SensorEntity): """Get the unit of measurement.""" return PERCENTAGE - async def async_update(self, now: datetime.datetime | None = None) -> None: + async def async_update(self) -> None: """Poll the device for the current state.""" await self.speaker.async_poll_battery() diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index bb6cb306426..81c95f6e33f 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -44,8 +44,8 @@ from .const import ( SONOS_CREATE_BATTERY, SONOS_CREATE_MEDIA_PLAYER, SONOS_ENTITY_CREATED, - SONOS_ENTITY_UPDATE, SONOS_GROUP_UPDATE, + SONOS_POLL_UPDATE, SONOS_SEEN, SONOS_STATE_PLAYING, SONOS_STATE_TRANSITIONING, @@ -138,6 +138,7 @@ class SonosSpeaker: self.household_id: str = soco.household_id self.media = SonosMedia(soco) + self.is_first_poll: bool = True self._is_ready: bool = False self._subscriptions: list[SubscriptionBase] = [] self._resubscription_lock: asyncio.Lock | None = None @@ -322,7 +323,7 @@ class SonosSpeaker: partial( async_dispatcher_send, self.hass, - f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}", + f"{SONOS_POLL_UPDATE}-{self.soco.uid}", ), SCAN_INTERVAL, ) @@ -418,7 +419,7 @@ class SonosSpeaker: ): async_dispatcher_send(self.hass, SONOS_CREATE_ALARM, self, new_alarms) - async_dispatcher_send(self.hass, SONOS_ALARM_UPDATE, self) + async_dispatcher_send(self.hass, SONOS_ALARM_UPDATE) async def async_update_battery_info(self, battery_dict: dict[str, Any]) -> None: """Update battery info using the decoded SonosEvent.""" @@ -875,7 +876,7 @@ class SonosSpeaker: if not self.media.artist: try: self.media.artist = variables["current_track_meta_data"].creator - except (KeyError, AttributeError): + except (TypeError, KeyError, AttributeError): pass # Radios without tagging can have part of the radio URI as title. @@ -948,3 +949,11 @@ class SonosSpeaker: elif update_media_position: self.media.position = current_position self.media.position_updated_at = dt_util.utcnow() + + @property + def subscription_address(self) -> str | None: + """Return the current subscription callback address if any.""" + if self._subscriptions: + addr, port = self._subscriptions[0].event_listener.address + return ":".join([addr, str(port)]) + return None diff --git a/homeassistant/components/sonos/switch.py b/homeassistant/components/sonos/switch.py index 879d5fa0a99..83449c846c6 100644 --- a/homeassistant/components/sonos/switch.py +++ b/homeassistant/components/sonos/switch.py @@ -112,7 +112,7 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity): return False - async def async_update(self, now: datetime.datetime | None = None) -> None: + async def async_update(self) -> None: """Poll the device for the current state.""" if await self.async_check_if_available(): await self.hass.async_add_executor_job(self.update_alarm)