mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
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
@ -1,7 +1,6 @@
|
|||||||
"""Entity representing a Sonos power sensor."""
|
"""Entity representing a Sonos power sensor."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@ -50,7 +49,7 @@ class SonosPowerEntity(SonosEntity, BinarySensorEntity):
|
|||||||
"""Return the entity's device class."""
|
"""Return the entity's device class."""
|
||||||
return DEVICE_CLASS_BATTERY_CHARGING
|
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."""
|
"""Poll the device for the current state."""
|
||||||
await self.speaker.async_poll_battery()
|
await self.speaker.async_poll_battery()
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ SONOS_CREATE_ALARM = "sonos_create_alarm"
|
|||||||
SONOS_CREATE_BATTERY = "sonos_create_battery"
|
SONOS_CREATE_BATTERY = "sonos_create_battery"
|
||||||
SONOS_CREATE_MEDIA_PLAYER = "sonos_create_media_player"
|
SONOS_CREATE_MEDIA_PLAYER = "sonos_create_media_player"
|
||||||
SONOS_ENTITY_CREATED = "sonos_entity_created"
|
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_GROUP_UPDATE = "sonos_group_update"
|
||||||
SONOS_HOUSEHOLD_UPDATED = "sonos_household_updated"
|
SONOS_HOUSEHOLD_UPDATED = "sonos_household_updated"
|
||||||
SONOS_ALARM_UPDATE = "sonos_alarm_update"
|
SONOS_ALARM_UPDATE = "sonos_alarm_update"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Entity representing a Sonos player."""
|
"""Entity representing a Sonos player."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pysonos.core import SoCo
|
from pysonos.core import SoCo
|
||||||
@ -15,8 +16,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
|
|||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SONOS_ENTITY_CREATED,
|
SONOS_ENTITY_CREATED,
|
||||||
SONOS_ENTITY_UPDATE,
|
|
||||||
SONOS_HOUSEHOLD_UPDATED,
|
SONOS_HOUSEHOLD_UPDATED,
|
||||||
|
SONOS_POLL_UPDATE,
|
||||||
SONOS_STATE_UPDATED,
|
SONOS_STATE_UPDATED,
|
||||||
)
|
)
|
||||||
from .speaker import SonosSpeaker
|
from .speaker import SonosSpeaker
|
||||||
@ -38,8 +39,8 @@ class SonosEntity(Entity):
|
|||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}",
|
f"{SONOS_POLL_UPDATE}-{self.soco.uid}",
|
||||||
self.async_update, # pylint: disable=no-member
|
self.async_poll,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
@ -60,6 +61,17 @@ class SonosEntity(Entity):
|
|||||||
self.hass, f"{SONOS_ENTITY_CREATED}-{self.soco.uid}", self.platform.domain
|
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
|
@property
|
||||||
def soco(self) -> SoCo:
|
def soco(self) -> SoCo:
|
||||||
"""Return the speaker SoCo instance."""
|
"""Return the speaker SoCo instance."""
|
||||||
|
@ -293,13 +293,12 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
|||||||
return STATE_PLAYING
|
return STATE_PLAYING
|
||||||
return STATE_IDLE
|
return STATE_IDLE
|
||||||
|
|
||||||
async def async_update(self, now: datetime.datetime | None = None) -> None:
|
async def async_update(self) -> None:
|
||||||
"""Retrieve latest state."""
|
"""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."""
|
"""Retrieve latest state."""
|
||||||
_LOGGER.debug("Polling speaker %s", self.speaker.zone_name)
|
|
||||||
try:
|
try:
|
||||||
self.speaker.update_groups()
|
self.speaker.update_groups()
|
||||||
self.speaker.update_volume()
|
self.speaker.update_volume()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Entity representing a Sonos battery level."""
|
"""Entity representing a Sonos battery level."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
@ -50,7 +49,7 @@ class SonosBatteryEntity(SonosEntity, SensorEntity):
|
|||||||
"""Get the unit of measurement."""
|
"""Get the unit of measurement."""
|
||||||
return PERCENTAGE
|
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."""
|
"""Poll the device for the current state."""
|
||||||
await self.speaker.async_poll_battery()
|
await self.speaker.async_poll_battery()
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ from .const import (
|
|||||||
SONOS_CREATE_BATTERY,
|
SONOS_CREATE_BATTERY,
|
||||||
SONOS_CREATE_MEDIA_PLAYER,
|
SONOS_CREATE_MEDIA_PLAYER,
|
||||||
SONOS_ENTITY_CREATED,
|
SONOS_ENTITY_CREATED,
|
||||||
SONOS_ENTITY_UPDATE,
|
|
||||||
SONOS_GROUP_UPDATE,
|
SONOS_GROUP_UPDATE,
|
||||||
|
SONOS_POLL_UPDATE,
|
||||||
SONOS_SEEN,
|
SONOS_SEEN,
|
||||||
SONOS_STATE_PLAYING,
|
SONOS_STATE_PLAYING,
|
||||||
SONOS_STATE_TRANSITIONING,
|
SONOS_STATE_TRANSITIONING,
|
||||||
@ -138,6 +138,7 @@ class SonosSpeaker:
|
|||||||
self.household_id: str = soco.household_id
|
self.household_id: str = soco.household_id
|
||||||
self.media = SonosMedia(soco)
|
self.media = SonosMedia(soco)
|
||||||
|
|
||||||
|
self.is_first_poll: bool = True
|
||||||
self._is_ready: bool = False
|
self._is_ready: bool = False
|
||||||
self._subscriptions: list[SubscriptionBase] = []
|
self._subscriptions: list[SubscriptionBase] = []
|
||||||
self._resubscription_lock: asyncio.Lock | None = None
|
self._resubscription_lock: asyncio.Lock | None = None
|
||||||
@ -322,7 +323,7 @@ class SonosSpeaker:
|
|||||||
partial(
|
partial(
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
self.hass,
|
self.hass,
|
||||||
f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}",
|
f"{SONOS_POLL_UPDATE}-{self.soco.uid}",
|
||||||
),
|
),
|
||||||
SCAN_INTERVAL,
|
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_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:
|
async def async_update_battery_info(self, battery_dict: dict[str, Any]) -> None:
|
||||||
"""Update battery info using the decoded SonosEvent."""
|
"""Update battery info using the decoded SonosEvent."""
|
||||||
@ -875,7 +876,7 @@ class SonosSpeaker:
|
|||||||
if not self.media.artist:
|
if not self.media.artist:
|
||||||
try:
|
try:
|
||||||
self.media.artist = variables["current_track_meta_data"].creator
|
self.media.artist = variables["current_track_meta_data"].creator
|
||||||
except (KeyError, AttributeError):
|
except (TypeError, KeyError, AttributeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Radios without tagging can have part of the radio URI as title.
|
# Radios without tagging can have part of the radio URI as title.
|
||||||
@ -948,3 +949,11 @@ class SonosSpeaker:
|
|||||||
elif update_media_position:
|
elif update_media_position:
|
||||||
self.media.position = current_position
|
self.media.position = current_position
|
||||||
self.media.position_updated_at = dt_util.utcnow()
|
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
|
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."""
|
"""Poll the device for the current state."""
|
||||||
if await self.async_check_if_available():
|
if await self.async_check_if_available():
|
||||||
await self.hass.async_add_executor_job(self.update_alarm)
|
await self.hass.async_add_executor_job(self.update_alarm)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user