diff --git a/homeassistant/components/webostv/__init__.py b/homeassistant/components/webostv/__init__.py index be0002cc588..410b3d853a1 100644 --- a/homeassistant/components/webostv/__init__.py +++ b/homeassistant/components/webostv/__init__.py @@ -4,72 +4,33 @@ from __future__ import annotations from contextlib import suppress import logging -from typing import NamedTuple from aiowebostv import WebOsClient, WebOsTvPairError -import voluptuous as vol from homeassistant.components import notify as hass_notify from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ATTR_COMMAND, - ATTR_ENTITY_ID, CONF_CLIENT_SECRET, CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP, ) -from homeassistant.core import Event, HomeAssistant, ServiceCall +from homeassistant.core import Event, HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers import config_validation as cv, discovery -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import ConfigType from .const import ( - ATTR_BUTTON, ATTR_CONFIG_ENTRY_ID, - ATTR_PAYLOAD, - ATTR_SOUND_OUTPUT, DATA_CONFIG_ENTRY, DATA_HASS_CONFIG, DOMAIN, PLATFORMS, - SERVICE_BUTTON, - SERVICE_COMMAND, - SERVICE_SELECT_SOUND_OUTPUT, WEBOSTV_EXCEPTIONS, ) CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) -CALL_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids}) - - -class ServiceMethodDetails(NamedTuple): - """Details for SERVICE_TO_METHOD mapping.""" - - method: str - schema: vol.Schema - - -BUTTON_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_BUTTON): cv.string}) - -COMMAND_SCHEMA = CALL_SCHEMA.extend( - {vol.Required(ATTR_COMMAND): cv.string, vol.Optional(ATTR_PAYLOAD): dict} -) - -SOUND_OUTPUT_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_SOUND_OUTPUT): cv.string}) - -SERVICE_TO_METHOD = { - SERVICE_BUTTON: ServiceMethodDetails(method="async_button", schema=BUTTON_SCHEMA), - SERVICE_COMMAND: ServiceMethodDetails( - method="async_command", schema=COMMAND_SCHEMA - ), - SERVICE_SELECT_SOUND_OUTPUT: ServiceMethodDetails( - method="async_select_sound_output", - schema=SOUND_OUTPUT_SCHEMA, - ), -} _LOGGER = logging.getLogger(__name__) @@ -100,17 +61,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Update the stored key without triggering reauth update_client_key(hass, entry, client) - async def async_service_handler(service: ServiceCall) -> None: - method = SERVICE_TO_METHOD[service.service] - data = service.data.copy() - data["method"] = method.method - async_dispatcher_send(hass, DOMAIN, data) - - for service, method in SERVICE_TO_METHOD.items(): - hass.services.async_register( - DOMAIN, service, async_service_handler, schema=method.schema - ) - hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = client await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -174,17 +124,10 @@ def update_client_key( async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - - if unload_ok: + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): client = hass.data[DOMAIN][DATA_CONFIG_ENTRY].pop(entry.entry_id) await hass_notify.async_reload(hass, DOMAIN) client.clear_state_update_callbacks() await client.disconnect() - # unregister service calls, check if this is the last entry to unload - if unload_ok and not hass.data[DOMAIN][DATA_CONFIG_ENTRY]: - for service in SERVICE_TO_METHOD: - hass.services.async_remove(DOMAIN, service) - return unload_ok diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 399acb9b44d..599eb48a69d 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -12,6 +12,7 @@ import logging from typing import Any, Concatenate, cast from aiowebostv import WebOsClient, WebOsTvPairError +import voluptuous as vol from homeassistant import util from homeassistant.components.media_player import ( @@ -22,29 +23,29 @@ from homeassistant.components.media_player import ( MediaType, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_ENTITY_ID, - ATTR_SUPPORTED_FEATURES, - ENTITY_MATCH_ALL, - ENTITY_MATCH_NONE, -) +from homeassistant.const import ATTR_COMMAND, ATTR_SUPPORTED_FEATURES from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.trigger import PluggableAction +from homeassistant.helpers.typing import VolDictType from . import update_client_key from .const import ( + ATTR_BUTTON, ATTR_PAYLOAD, ATTR_SOUND_OUTPUT, CONF_SOURCES, DATA_CONFIG_ENTRY, DOMAIN, LIVE_TV_APP_ID, + SERVICE_BUTTON, + SERVICE_COMMAND, + SERVICE_SELECT_SOUND_OUTPUT, WEBOSTV_EXCEPTIONS, ) from .triggers.turn_on import async_get_turn_on_trigger @@ -71,11 +72,29 @@ MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) PARALLEL_UPDATES = 0 SCAN_INTERVAL = timedelta(seconds=10) +BUTTON_SCHEMA: VolDictType = {vol.Required(ATTR_BUTTON): cv.string} +COMMAND_SCHEMA: VolDictType = { + vol.Required(ATTR_COMMAND): cv.string, + vol.Optional(ATTR_PAYLOAD): dict, +} +SOUND_OUTPUT_SCHEMA: VolDictType = {vol.Required(ATTR_SOUND_OUTPUT): cv.string} + +SERVICES = ( + (SERVICE_BUTTON, BUTTON_SCHEMA, "async_button"), + (SERVICE_COMMAND, COMMAND_SCHEMA, "async_command"), + (SERVICE_SELECT_SOUND_OUTPUT, SOUND_OUTPUT_SCHEMA, "async_select_sound_output"), +) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the LG webOS Smart TV platform.""" + platform = entity_platform.async_get_current_platform() + + for service_name, schema, method in SERVICES: + platform.async_register_entity_service(service_name, schema, method) + client = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] async_add_entities([LgWebOSMediaPlayerEntity(entry, client)]) @@ -143,10 +162,6 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): ) ) - self.async_on_remove( - async_dispatcher_connect(self.hass, DOMAIN, self.async_signal_handler) - ) - await self._client.register_state_update_callback( self.async_handle_state_update ) @@ -166,19 +181,6 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): """Call disconnect on removal.""" self._client.unregister_state_update_callback(self.async_handle_state_update) - async def async_signal_handler(self, data: dict[str, Any]) -> None: - """Handle domain-specific signal by calling appropriate method.""" - if (entity_ids := data[ATTR_ENTITY_ID]) == ENTITY_MATCH_NONE: - return - - if entity_ids == ENTITY_MATCH_ALL or self.entity_id in entity_ids: - params = { - key: value - for key, value in data.items() - if key not in ["entity_id", "method"] - } - await getattr(self, data["method"])(**params) - async def async_handle_state_update(self, _client: WebOsClient) -> None: """Update state from WebOsClient.""" self._update_states() diff --git a/homeassistant/components/webostv/quality_scale.yaml b/homeassistant/components/webostv/quality_scale.yaml index a5d898b1de7..1bf521ef2e2 100644 --- a/homeassistant/components/webostv/quality_scale.yaml +++ b/homeassistant/components/webostv/quality_scale.yaml @@ -1,8 +1,6 @@ rules: # Bronze - action-setup: - status: todo - comment: move actions to entity services + action-setup: done appropriate-polling: done brands: done common-modules: