Improve Sonos polling (#51170)
* Improve Sonos polling Warn user if polling is being used Provide callback IP:port to help user fix networking Fix radio handling when polling (no event payload) Clarify dispatch target to reflect polling action * Lint * Revert method removal
This commit is contained in:
parent
e45196f9c9
commit
39e62f9c90
7 changed files with 35 additions and 17 deletions
|
@ -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()
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue