Add Sonos favorites sensor (#70235)
parent
9bec649323
commit
ac88d0be14
|
@ -152,6 +152,7 @@ SONOS_CHECK_ACTIVITY = "sonos_check_activity"
|
|||
SONOS_CREATE_ALARM = "sonos_create_alarm"
|
||||
SONOS_CREATE_AUDIO_FORMAT_SENSOR = "sonos_create_audio_format_sensor"
|
||||
SONOS_CREATE_BATTERY = "sonos_create_battery"
|
||||
SONOS_CREATE_FAVORITES_SENSOR = "sonos_create_favorites_sensor"
|
||||
SONOS_CREATE_MIC_SENSOR = "sonos_create_mic_sensor"
|
||||
SONOS_CREATE_SWITCHES = "sonos_create_switches"
|
||||
SONOS_CREATE_LEVELS = "sonos_create_levels"
|
||||
|
|
|
@ -13,13 +13,7 @@ import homeassistant.helpers.device_registry as dr
|
|||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from .const import (
|
||||
DATA_SONOS,
|
||||
DOMAIN,
|
||||
SONOS_FALLBACK_POLL,
|
||||
SONOS_FAVORITES_UPDATED,
|
||||
SONOS_STATE_UPDATED,
|
||||
)
|
||||
from .const import DATA_SONOS, DOMAIN, SONOS_FALLBACK_POLL, SONOS_STATE_UPDATED
|
||||
from .exception import SonosUpdateError
|
||||
from .speaker import SonosSpeaker
|
||||
|
||||
|
@ -54,13 +48,6 @@ class SonosEntity(Entity):
|
|||
self.async_write_ha_state,
|
||||
)
|
||||
)
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"{SONOS_FAVORITES_UPDATED}-{self.soco.household_id}",
|
||||
self.async_write_ha_state,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Clean up when entity is removed."""
|
||||
|
|
|
@ -11,9 +11,9 @@ from soco.data_structures import DidlFavorite
|
|||
from soco.events_base import Event as SonosEvent
|
||||
from soco.exceptions import SoCoException
|
||||
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send, dispatcher_send
|
||||
|
||||
from .const import SONOS_FAVORITES_UPDATED
|
||||
from .const import SONOS_CREATE_FAVORITES_SENSOR, SONOS_FAVORITES_UPDATED
|
||||
from .helpers import soco_error
|
||||
from .household_coordinator import SonosHouseholdCoordinator
|
||||
|
||||
|
@ -37,6 +37,16 @@ class SonosFavorites(SonosHouseholdCoordinator):
|
|||
favorites = self._favorites.copy()
|
||||
return iter(favorites)
|
||||
|
||||
def setup(self, soco: SoCo) -> None:
|
||||
"""Override to send a signal on base class setup completion."""
|
||||
super().setup(soco)
|
||||
dispatcher_send(self.hass, SONOS_CREATE_FAVORITES_SENSOR, self)
|
||||
|
||||
@property
|
||||
def count(self) -> int:
|
||||
"""Return the number of favorites."""
|
||||
return len(self._favorites)
|
||||
|
||||
def lookup_by_item_id(self, item_id: str) -> DidlFavorite | None:
|
||||
"""Return the favorite object with the provided item_id."""
|
||||
return next((fav for fav in self._favorites if fav.item_id == item_id), None)
|
||||
|
|
|
@ -11,8 +11,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import SONOS_CREATE_AUDIO_FORMAT_SENSOR, SONOS_CREATE_BATTERY, SOURCE_TV
|
||||
from .const import (
|
||||
SONOS_CREATE_AUDIO_FORMAT_SENSOR,
|
||||
SONOS_CREATE_BATTERY,
|
||||
SONOS_CREATE_FAVORITES_SENSOR,
|
||||
SONOS_FAVORITES_UPDATED,
|
||||
SOURCE_TV,
|
||||
)
|
||||
from .entity import SonosEntity, SonosPollingEntity
|
||||
from .favorites import SonosFavorites
|
||||
from .helpers import soco_error
|
||||
from .speaker import SonosSpeaker
|
||||
|
||||
|
@ -40,6 +47,16 @@ async def async_setup_entry(
|
|||
entity = SonosBatteryEntity(speaker)
|
||||
async_add_entities([entity])
|
||||
|
||||
@callback
|
||||
def _async_create_favorites_sensor(favorites: SonosFavorites) -> None:
|
||||
_LOGGER.debug(
|
||||
"Creating favorites sensor (%s items) for household %s",
|
||||
favorites.count,
|
||||
favorites.household_id,
|
||||
)
|
||||
entity = SonosFavoritesEntity(favorites)
|
||||
async_add_entities([entity])
|
||||
|
||||
config_entry.async_on_unload(
|
||||
async_dispatcher_connect(
|
||||
hass, SONOS_CREATE_AUDIO_FORMAT_SENSOR, _async_create_audio_format_entity
|
||||
|
@ -51,6 +68,12 @@ async def async_setup_entry(
|
|||
)
|
||||
)
|
||||
|
||||
config_entry.async_on_unload(
|
||||
async_dispatcher_connect(
|
||||
hass, SONOS_CREATE_FAVORITES_SENSOR, _async_create_favorites_sensor
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SonosBatteryEntity(SonosEntity, SensorEntity):
|
||||
"""Representation of a Sonos Battery entity."""
|
||||
|
@ -107,3 +130,36 @@ class SonosAudioInputFormatSensorEntity(SonosPollingEntity, SensorEntity):
|
|||
|
||||
async def _async_fallback_poll(self) -> None:
|
||||
"""Provide a stub for required ABC method."""
|
||||
|
||||
|
||||
class SonosFavoritesEntity(SensorEntity):
|
||||
"""Representation of a Sonos favorites info entity."""
|
||||
|
||||
_attr_entity_registry_enabled_default = False
|
||||
_attr_icon = "mdi:star"
|
||||
_attr_name = "Sonos Favorites"
|
||||
_attr_native_unit_of_measurement = "items"
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, favorites: SonosFavorites) -> None:
|
||||
"""Initialize the favorites sensor."""
|
||||
self.favorites = favorites
|
||||
self._attr_unique_id = f"{favorites.household_id}-favorites"
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle common setup when added to hass."""
|
||||
await self._async_update_state()
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"{SONOS_FAVORITES_UPDATED}-{self.favorites.household_id}",
|
||||
self._async_update_state,
|
||||
)
|
||||
)
|
||||
|
||||
async def _async_update_state(self) -> None:
|
||||
self._attr_native_value = self.favorites.count
|
||||
self._attr_extra_state_attributes = {
|
||||
"items": {fav.item_id: fav.title for fav in self.favorites}
|
||||
}
|
||||
self.async_write_ha_state()
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
"""Tests for the Sonos battery sensor platform."""
|
||||
from unittest.mock import PropertyMock
|
||||
from datetime import timedelta
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from soco.exceptions import NotSupportedException
|
||||
|
||||
from homeassistant.components.sensor import SCAN_INTERVAL
|
||||
from homeassistant.components.sonos.binary_sensor import ATTR_BATTERY_POWER_SOURCE
|
||||
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers import entity_registry as ent_reg
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .conftest import SonosMockEvent
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
|
@ -178,3 +182,38 @@ async def test_microphone_binary_sensor(
|
|||
|
||||
mic_binary_sensor_state = hass.states.get(mic_binary_sensor.entity_id)
|
||||
assert mic_binary_sensor_state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_favorites_sensor(hass, async_autosetup_sonos, soco):
|
||||
"""Test Sonos favorites sensor."""
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
favorites = entity_registry.entities["sensor.sonos_favorites"]
|
||||
assert hass.states.get(favorites.entity_id) is None
|
||||
|
||||
# Enable disabled sensor
|
||||
entity_registry.async_update_entity(entity_id=favorites.entity_id, disabled_by=None)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Fire event to cancel poll timer and avoid triggering errors during time jump
|
||||
service = soco.contentDirectory
|
||||
empty_event = SonosMockEvent(soco, service, {})
|
||||
subscription = service.subscribe.return_value
|
||||
subscription.callback(event=empty_event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Reload the integration to enable the sensor
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
favorites_updated_event = SonosMockEvent(
|
||||
soco, service, {"favorites_update_id": "2", "container_update_i_ds": "FV:2,2"}
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.sonos.favorites.SonosFavorites.update_cache",
|
||||
return_value=True,
|
||||
):
|
||||
subscription.callback(event=favorites_updated_event)
|
||||
await hass.async_block_till_done()
|
||||
|
|
Loading…
Reference in New Issue