diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 7a9d994737d..dbbeecdcdb3 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio import datetime -from functools import partial import logging import socket @@ -64,14 +63,13 @@ CONFIG_SCHEMA = vol.Schema( class SonosData: """Storage class for platform global data.""" - def __init__(self): + def __init__(self) -> None: """Initialize the data.""" - self.discovered = {} + self.discovered: dict[str, SonosSpeaker] = {} self.media_player_entities = {} self.topology_condition = asyncio.Condition() self.discovery_thread = None self.hosts_heartbeat = None - self.platforms_ready = set() async def async_setup(hass, config): @@ -90,7 +88,7 @@ async def async_setup(hass, config): return True -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Sonos from a config entry.""" pysonos.config.EVENTS_MODULE = events_asyncio @@ -168,25 +166,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: def _async_signal_update_groups(event): async_dispatcher_send(hass, SONOS_GROUP_UPDATE) - @callback - def start_discovery(): + async def setup_platforms_and_discovery(): + await asyncio.gather( + *[ + hass.config_entries.async_forward_entry_setup(entry, platform) + for platform in PLATFORMS + ] + ) + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_discovery) + ) + entry.async_on_unload( + hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, _async_signal_update_groups + ) + ) _LOGGER.debug("Adding discovery job") - hass.async_add_executor_job(_discovery) - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_discovery) - hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_START, _async_signal_update_groups - ) + await hass.async_add_executor_job(_discovery) - @callback - def platform_ready(platform, _): - hass.data[DATA_SONOS].platforms_ready.add(platform) - if hass.data[DATA_SONOS].platforms_ready == PLATFORMS: - start_discovery() - - for platform in PLATFORMS: - task = hass.async_create_task( - hass.config_entries.async_forward_entry_setup(entry, platform) - ) - task.add_done_callback(partial(platform_ready, platform)) + hass.async_create_task(setup_platforms_and_discovery()) return True diff --git a/homeassistant/components/sonos/config_flow.py b/homeassistant/components/sonos/config_flow.py index 42ac32163a4..6807cffa373 100644 --- a/homeassistant/components/sonos/config_flow.py +++ b/homeassistant/components/sonos/config_flow.py @@ -2,14 +2,16 @@ import pysonos from homeassistant import config_entries +from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_flow from .const import DOMAIN -async def _async_has_devices(hass): +async def _async_has_devices(hass: HomeAssistant) -> bool: """Return if there are devices that can be discovered.""" - return await hass.async_add_executor_job(pysonos.discover) + result = await hass.async_add_executor_job(pysonos.discover) + return bool(result) config_entry_flow.register_discovery_flow( diff --git a/homeassistant/components/sonos/entity.py b/homeassistant/components/sonos/entity.py index 69a88077e31..159b3fb348a 100644 --- a/homeassistant/components/sonos/entity.py +++ b/homeassistant/components/sonos/entity.py @@ -6,7 +6,6 @@ from typing import Any from pysonos.core import SoCo -from homeassistant.core import callback import homeassistant.helpers.device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity @@ -21,7 +20,7 @@ _LOGGER = logging.getLogger(__name__) class SonosEntity(Entity): """Representation of a Sonos entity.""" - def __init__(self, speaker: SonosSpeaker, sonos_data: SonosData): + def __init__(self, speaker: SonosSpeaker, sonos_data: SonosData) -> None: """Initialize a SonosEntity.""" self.speaker = speaker self.data = sonos_data @@ -41,7 +40,7 @@ class SonosEntity(Entity): async_dispatcher_connect( self.hass, f"{SONOS_STATE_UPDATED}-{self.soco.uid}", - self.async_write_state, + self.async_write_ha_state, ) ) @@ -72,8 +71,3 @@ class SonosEntity(Entity): def should_poll(self) -> bool: """Return that we should not be polled (we handle that internally).""" return False - - @callback - def async_write_state(self) -> None: - """Flush the current entity state.""" - self.async_write_ha_state() diff --git a/homeassistant/components/sonos/sensor.py b/homeassistant/components/sonos/sensor.py index 67c5040a4a4..2ca5e0979dc 100644 --- a/homeassistant/components/sonos/sensor.py +++ b/homeassistant/components/sonos/sensor.py @@ -10,14 +10,12 @@ from pysonos.core import SoCo from pysonos.events_base import Event as SonosEvent from pysonos.exceptions import SoCoException -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorEntity from homeassistant.const import DEVICE_CLASS_BATTERY, PERCENTAGE, STATE_UNKNOWN from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.util import dt as dt_util from . import SonosData @@ -51,6 +49,7 @@ def fetch_battery_info_or_none(soco: SoCo) -> dict[str, Any] | None: """ with contextlib.suppress(ConnectionError, TimeoutError, SoCoException): return soco.get_battery_info() + return None async def async_setup_entry(hass, config_entry, async_add_entities): @@ -76,16 +75,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_dispatcher_connect(hass, SONOS_DISCOVERY_UPDATE, _async_create_entities) -class SonosBatteryEntity(SonosEntity, Entity): +class SonosBatteryEntity(SonosEntity, SensorEntity): """Representation of a Sonos Battery entity.""" def __init__( self, speaker: SonosSpeaker, sonos_data: SonosData, battery_info: dict[str, Any] - ): + ) -> None: """Initialize a SonosBatteryEntity.""" super().__init__(speaker, sonos_data) self._battery_info: dict[str, Any] = battery_info - self._last_event: datetime.datetime = None + self._last_event: datetime.datetime | None = None async def async_added_to_hass(self) -> None: """Register polling callback when added to hass.""" @@ -185,11 +184,6 @@ class SonosBatteryEntity(SonosEntity, Entity): """Return the charging status of this battery.""" return self.power_source not in ("BATTERY", STATE_UNKNOWN) - @property - def icon(self) -> str: - """Return the icon of the sensor.""" - return icon_for_battery_level(self.battery_level, self.charging) - @property def state(self) -> int | None: """Return the state of the sensor.""" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index b2e53755da5..2d67cf8041f 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -40,7 +40,9 @@ _LOGGER = logging.getLogger(__name__) class SonosSpeaker: """Representation of a Sonos speaker.""" - def __init__(self, hass: HomeAssistant, soco: SoCo, speaker_info: dict[str, Any]): + def __init__( + self, hass: HomeAssistant, soco: SoCo, speaker_info: dict[str, Any] + ) -> None: """Initialize a SonosSpeaker.""" self._is_ready: bool = False self._subscriptions: list[SubscriptionBase] = [] @@ -78,7 +80,7 @@ class SonosSpeaker: self._is_ready = True @callback - def async_write_entity_states(self) -> bool: + def async_write_entity_states(self) -> None: """Write states for associated SonosEntity instances.""" async_dispatcher_send(self.hass, f"{SONOS_STATE_UPDATED}-{self.soco.uid}") diff --git a/tests/components/sonos/test_sensor.py b/tests/components/sonos/test_sensor.py index 3752af7f377..a1fc1d7efd8 100644 --- a/tests/components/sonos/test_sensor.py +++ b/tests/components/sonos/test_sensor.py @@ -57,6 +57,5 @@ async def test_battery_attributes(hass, config_entry, config, soco): # confirm initial state from conftest assert battery_state.state == "100" assert battery_state.attributes.get("unit_of_measurement") == "%" - assert battery_state.attributes.get("icon") == "mdi:battery-charging-100" assert battery_state.attributes.get("charging") assert battery_state.attributes.get("power_source") == "SONOS_CHARGING_RING"