diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index a6bc24099f0..65314439c18 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -24,12 +24,10 @@ from pyheos import ( const as heos_const, ) from pyheos.util import mediauri as heos_source -import voluptuous as vol from homeassistant.components import media_source from homeassistant.components.media_player import ( ATTR_MEDIA_ENQUEUE, - ATTR_MEDIA_VOLUME_LEVEL, BrowseError, BrowseMedia, MediaClass, @@ -43,32 +41,16 @@ from homeassistant.components.media_player import ( ) from homeassistant.components.media_source import BrowseMediaSource from homeassistant.const import Platform -from homeassistant.core import ( - HomeAssistant, - ServiceResponse, - SupportsResponse, - callback, -) +from homeassistant.core import HomeAssistant, ServiceResponse, callback from homeassistant.exceptions import HomeAssistantError, ServiceValidationError -from homeassistant.helpers import ( - config_validation as cv, - entity_platform, - entity_registry as er, -) +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util.dt import utcnow -from .const import ( - ATTR_QUEUE_IDS, - DOMAIN as HEOS_DOMAIN, - SERVICE_GET_QUEUE, - SERVICE_GROUP_VOLUME_DOWN, - SERVICE_GROUP_VOLUME_SET, - SERVICE_GROUP_VOLUME_UP, - SERVICE_REMOVE_FROM_QUEUE, -) +from . import services +from .const import DOMAIN as HEOS_DOMAIN from .coordinator import HeosConfigEntry, HeosCoordinator PARALLEL_UPDATES = 0 @@ -139,36 +121,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Add media players for a config entry.""" - # Register custom entity services - platform = entity_platform.async_get_current_platform() - platform.async_register_entity_service( - SERVICE_GET_QUEUE, - None, - "async_get_queue", - supports_response=SupportsResponse.ONLY, - ) - platform.async_register_entity_service( - SERVICE_REMOVE_FROM_QUEUE, - { - vol.Required(ATTR_QUEUE_IDS): vol.All( - cv.ensure_list, - [vol.All(cv.positive_int, vol.Range(min=1))], - vol.Unique(), - ) - }, - "async_remove_from_queue", - ) - platform.async_register_entity_service( - SERVICE_GROUP_VOLUME_SET, - {vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float}, - "async_set_group_volume_level", - ) - platform.async_register_entity_service( - SERVICE_GROUP_VOLUME_DOWN, None, "async_group_volume_down" - ) - platform.async_register_entity_service( - SERVICE_GROUP_VOLUME_UP, None, "async_group_volume_up" - ) + services.register_media_player_services() def add_entities_callback(players: Sequence[HeosPlayer]) -> None: """Add entities for each player.""" diff --git a/homeassistant/components/heos/services.py b/homeassistant/components/heos/services.py index dc11bb7a76d..fe8c887691c 100644 --- a/homeassistant/components/heos/services.py +++ b/homeassistant/components/heos/services.py @@ -1,19 +1,33 @@ """Services for the HEOS integration.""" +from dataclasses import dataclass import logging +from typing import Final from pyheos import CommandAuthenticationError, Heos, HeosError import voluptuous as vol +from homeassistant.components.media_player import ATTR_MEDIA_VOLUME_LEVEL from homeassistant.config_entries import ConfigEntryState -from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse from homeassistant.exceptions import HomeAssistantError, ServiceValidationError -from homeassistant.helpers import config_validation as cv, issue_registry as ir +from homeassistant.helpers import ( + config_validation as cv, + entity_platform, + issue_registry as ir, +) +from homeassistant.helpers.typing import VolDictType, VolSchemaType from .const import ( ATTR_PASSWORD, + ATTR_QUEUE_IDS, ATTR_USERNAME, DOMAIN, + SERVICE_GET_QUEUE, + SERVICE_GROUP_VOLUME_DOWN, + SERVICE_GROUP_VOLUME_SET, + SERVICE_GROUP_VOLUME_UP, + SERVICE_REMOVE_FROM_QUEUE, SERVICE_SIGN_IN, SERVICE_SIGN_OUT, ) @@ -44,6 +58,62 @@ def register(hass: HomeAssistant) -> None: ) +@dataclass(frozen=True) +class EntityServiceDescription: + """Describe an entity service.""" + + name: str + method_name: str + schema: VolDictType | VolSchemaType | None = None + supports_response: SupportsResponse = SupportsResponse.NONE + + def async_register(self, platform: entity_platform.EntityPlatform) -> None: + """Register the service with the platform.""" + platform.async_register_entity_service( + self.name, + self.schema, + self.method_name, + supports_response=self.supports_response, + ) + + +REMOVE_FROM_QUEUE_SCHEMA: Final[VolDictType] = { + vol.Required(ATTR_QUEUE_IDS): vol.All( + cv.ensure_list, + [vol.All(cv.positive_int, vol.Range(min=1))], + vol.Unique(), + ) +} +GROUP_VOLUME_SET_SCHEMA: Final[VolDictType] = { + vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float +} + +MEDIA_PLAYER_ENTITY_SERVICES: Final = ( + # Player queue services + EntityServiceDescription( + SERVICE_GET_QUEUE, "async_get_queue", supports_response=SupportsResponse.ONLY + ), + EntityServiceDescription( + SERVICE_REMOVE_FROM_QUEUE, "async_remove_from_queue", REMOVE_FROM_QUEUE_SCHEMA + ), + # Group volume services + EntityServiceDescription( + SERVICE_GROUP_VOLUME_SET, + "async_set_group_volume_level", + GROUP_VOLUME_SET_SCHEMA, + ), + EntityServiceDescription(SERVICE_GROUP_VOLUME_DOWN, "async_group_volume_down"), + EntityServiceDescription(SERVICE_GROUP_VOLUME_UP, "async_group_volume_up"), +) + + +def register_media_player_services() -> None: + """Register media_player entity services.""" + platform = entity_platform.async_get_current_platform() + for service in MEDIA_PLAYER_ENTITY_SERVICES: + service.async_register(platform) + + def _get_controller(hass: HomeAssistant) -> Heos: """Get the HEOS controller instance.""" _LOGGER.warning(