Improve SERVICE_TO_METHOD typing (#120474)

This commit is contained in:
Marc Mueller 2024-06-26 02:20:48 +02:00 committed by GitHub
parent 0bc597f8c7
commit 3937cc2963
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 54 deletions

View File

@ -1651,6 +1651,7 @@ omit =
homeassistant/components/xiaomi_miio/remote.py homeassistant/components/xiaomi_miio/remote.py
homeassistant/components/xiaomi_miio/sensor.py homeassistant/components/xiaomi_miio/sensor.py
homeassistant/components/xiaomi_miio/switch.py homeassistant/components/xiaomi_miio/switch.py
homeassistant/components/xiaomi_miio/typing.py
homeassistant/components/xiaomi_tv/media_player.py homeassistant/components/xiaomi_tv/media_player.py
homeassistant/components/xmpp/notify.py homeassistant/components/xmpp/notify.py
homeassistant/components/xs1/* homeassistant/components/xs1/*

View File

@ -7,7 +7,7 @@ from asyncio import CancelledError, timeout
from datetime import timedelta from datetime import timedelta
from http import HTTPStatus from http import HTTPStatus
import logging import logging
from typing import Any from typing import Any, NamedTuple
from urllib import parse from urllib import parse
import aiohttp import aiohttp
@ -85,15 +85,27 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
} }
) )
class ServiceMethodDetails(NamedTuple):
"""Details for SERVICE_TO_METHOD mapping."""
method: str
schema: vol.Schema
BS_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids}) BS_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids})
BS_JOIN_SCHEMA = BS_SCHEMA.extend({vol.Required(ATTR_MASTER): cv.entity_id}) BS_JOIN_SCHEMA = BS_SCHEMA.extend({vol.Required(ATTR_MASTER): cv.entity_id})
SERVICE_TO_METHOD = { SERVICE_TO_METHOD = {
SERVICE_JOIN: {"method": "async_join", "schema": BS_JOIN_SCHEMA}, SERVICE_JOIN: ServiceMethodDetails(method="async_join", schema=BS_JOIN_SCHEMA),
SERVICE_UNJOIN: {"method": "async_unjoin", "schema": BS_SCHEMA}, SERVICE_UNJOIN: ServiceMethodDetails(method="async_unjoin", schema=BS_SCHEMA),
SERVICE_SET_TIMER: {"method": "async_increase_timer", "schema": BS_SCHEMA}, SERVICE_SET_TIMER: ServiceMethodDetails(
SERVICE_CLEAR_TIMER: {"method": "async_clear_timer", "schema": BS_SCHEMA}, method="async_increase_timer", schema=BS_SCHEMA
),
SERVICE_CLEAR_TIMER: ServiceMethodDetails(
method="async_clear_timer", schema=BS_SCHEMA
),
} }
@ -188,12 +200,11 @@ async def async_setup_platform(
target_players = hass.data[DATA_BLUESOUND] target_players = hass.data[DATA_BLUESOUND]
for player in target_players: for player in target_players:
await getattr(player, method["method"])(**params) await getattr(player, method.method)(**params)
for service, method in SERVICE_TO_METHOD.items(): for service, method in SERVICE_TO_METHOD.items():
schema = method["schema"]
hass.services.async_register( hass.services.async_register(
DOMAIN, service, async_service_handler, schema=schema DOMAIN, service, async_service_handler, schema=method.schema
) )

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from contextlib import suppress from contextlib import suppress
import logging import logging
from typing import NamedTuple
from aiowebostv import WebOsClient, WebOsTvPairError from aiowebostv import WebOsClient, WebOsTvPairError
import voluptuous as vol import voluptuous as vol
@ -43,6 +44,14 @@ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
CALL_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids}) 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}) BUTTON_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_BUTTON): cv.string})
COMMAND_SCHEMA = CALL_SCHEMA.extend( COMMAND_SCHEMA = CALL_SCHEMA.extend(
@ -52,12 +61,14 @@ COMMAND_SCHEMA = CALL_SCHEMA.extend(
SOUND_OUTPUT_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_SOUND_OUTPUT): cv.string}) SOUND_OUTPUT_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_SOUND_OUTPUT): cv.string})
SERVICE_TO_METHOD = { SERVICE_TO_METHOD = {
SERVICE_BUTTON: {"method": "async_button", "schema": BUTTON_SCHEMA}, SERVICE_BUTTON: ServiceMethodDetails(method="async_button", schema=BUTTON_SCHEMA),
SERVICE_COMMAND: {"method": "async_command", "schema": COMMAND_SCHEMA}, SERVICE_COMMAND: ServiceMethodDetails(
SERVICE_SELECT_SOUND_OUTPUT: { method="async_command", schema=COMMAND_SCHEMA
"method": "async_select_sound_output", ),
"schema": SOUND_OUTPUT_SCHEMA, SERVICE_SELECT_SOUND_OUTPUT: ServiceMethodDetails(
}, method="async_select_sound_output",
schema=SOUND_OUTPUT_SCHEMA,
),
} }
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -92,13 +103,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_service_handler(service: ServiceCall) -> None: async def async_service_handler(service: ServiceCall) -> None:
method = SERVICE_TO_METHOD[service.service] method = SERVICE_TO_METHOD[service.service]
data = service.data.copy() data = service.data.copy()
data["method"] = method["method"] data["method"] = method.method
async_dispatcher_send(hass, DOMAIN, data) async_dispatcher_send(hass, DOMAIN, data)
for service, method in SERVICE_TO_METHOD.items(): for service, method in SERVICE_TO_METHOD.items():
schema = method["schema"]
hass.services.async_register( hass.services.async_register(
DOMAIN, service, async_service_handler, schema=schema DOMAIN, service, async_service_handler, schema=method.schema
) )
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = client hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = client

View File

@ -92,6 +92,7 @@ from .const import (
SERVICE_SET_EXTRA_FEATURES, SERVICE_SET_EXTRA_FEATURES,
) )
from .device import XiaomiCoordinatedMiioEntity from .device import XiaomiCoordinatedMiioEntity
from .typing import ServiceMethodDetails
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -182,11 +183,11 @@ SERVICE_SCHEMA_EXTRA_FEATURES = AIRPURIFIER_SERVICE_SCHEMA.extend(
) )
SERVICE_TO_METHOD = { SERVICE_TO_METHOD = {
SERVICE_RESET_FILTER: {"method": "async_reset_filter"}, SERVICE_RESET_FILTER: ServiceMethodDetails(method="async_reset_filter"),
SERVICE_SET_EXTRA_FEATURES: { SERVICE_SET_EXTRA_FEATURES: ServiceMethodDetails(
"method": "async_set_extra_features", method="async_set_extra_features",
"schema": SERVICE_SCHEMA_EXTRA_FEATURES, schema=SERVICE_SCHEMA_EXTRA_FEATURES,
}, ),
} }
FAN_DIRECTIONS_MAP = { FAN_DIRECTIONS_MAP = {
@ -271,7 +272,7 @@ async def async_setup_entry(
update_tasks = [] update_tasks = []
for entity in filtered_entities: for entity in filtered_entities:
entity_method = getattr(entity, method["method"], None) entity_method = getattr(entity, method.method, None)
if not entity_method: if not entity_method:
continue continue
await entity_method(**params) await entity_method(**params)
@ -281,7 +282,7 @@ async def async_setup_entry(
await asyncio.wait(update_tasks) await asyncio.wait(update_tasks)
for air_purifier_service, method in SERVICE_TO_METHOD.items(): for air_purifier_service, method in SERVICE_TO_METHOD.items():
schema = method.get("schema", AIRPURIFIER_SERVICE_SCHEMA) schema = method.schema or AIRPURIFIER_SERVICE_SCHEMA
hass.services.async_register( hass.services.async_register(
DOMAIN, air_purifier_service, async_service_handler, schema=schema DOMAIN, air_purifier_service, async_service_handler, schema=schema
) )

View File

@ -68,6 +68,7 @@ from .const import (
) )
from .device import XiaomiMiioEntity from .device import XiaomiMiioEntity
from .gateway import XiaomiGatewayDevice from .gateway import XiaomiGatewayDevice
from .typing import ServiceMethodDetails
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -108,20 +109,24 @@ SERVICE_SCHEMA_SET_DELAYED_TURN_OFF = XIAOMI_MIIO_SERVICE_SCHEMA.extend(
) )
SERVICE_TO_METHOD = { SERVICE_TO_METHOD = {
SERVICE_SET_DELAYED_TURN_OFF: { SERVICE_SET_DELAYED_TURN_OFF: ServiceMethodDetails(
"method": "async_set_delayed_turn_off", method="async_set_delayed_turn_off",
"schema": SERVICE_SCHEMA_SET_DELAYED_TURN_OFF, schema=SERVICE_SCHEMA_SET_DELAYED_TURN_OFF,
}, ),
SERVICE_SET_SCENE: { SERVICE_SET_SCENE: ServiceMethodDetails(
"method": "async_set_scene", method="async_set_scene",
"schema": SERVICE_SCHEMA_SET_SCENE, schema=SERVICE_SCHEMA_SET_SCENE,
}, ),
SERVICE_REMINDER_ON: {"method": "async_reminder_on"}, SERVICE_REMINDER_ON: ServiceMethodDetails(method="async_reminder_on"),
SERVICE_REMINDER_OFF: {"method": "async_reminder_off"}, SERVICE_REMINDER_OFF: ServiceMethodDetails(method="async_reminder_off"),
SERVICE_NIGHT_LIGHT_MODE_ON: {"method": "async_night_light_mode_on"}, SERVICE_NIGHT_LIGHT_MODE_ON: ServiceMethodDetails(
SERVICE_NIGHT_LIGHT_MODE_OFF: {"method": "async_night_light_mode_off"}, method="async_night_light_mode_on"
SERVICE_EYECARE_MODE_ON: {"method": "async_eyecare_mode_on"}, ),
SERVICE_EYECARE_MODE_OFF: {"method": "async_eyecare_mode_off"}, SERVICE_NIGHT_LIGHT_MODE_OFF: ServiceMethodDetails(
method="async_night_light_mode_off"
),
SERVICE_EYECARE_MODE_ON: ServiceMethodDetails(method="async_eyecare_mode_on"),
SERVICE_EYECARE_MODE_OFF: ServiceMethodDetails(method="async_eyecare_mode_off"),
} }
@ -232,9 +237,9 @@ async def async_setup_entry(
update_tasks = [] update_tasks = []
for target_device in target_devices: for target_device in target_devices:
if not hasattr(target_device, method["method"]): if not hasattr(target_device, method.method):
continue continue
await getattr(target_device, method["method"])(**params) await getattr(target_device, method.method)(**params)
update_tasks.append( update_tasks.append(
asyncio.create_task(target_device.async_update_ha_state(True)) asyncio.create_task(target_device.async_update_ha_state(True))
) )
@ -243,7 +248,7 @@ async def async_setup_entry(
await asyncio.wait(update_tasks) await asyncio.wait(update_tasks)
for xiaomi_miio_service, method in SERVICE_TO_METHOD.items(): for xiaomi_miio_service, method in SERVICE_TO_METHOD.items():
schema = method.get("schema", XIAOMI_MIIO_SERVICE_SCHEMA) schema = method.schema or XIAOMI_MIIO_SERVICE_SCHEMA
hass.services.async_register( hass.services.async_register(
DOMAIN, xiaomi_miio_service, async_service_handler, schema=schema DOMAIN, xiaomi_miio_service, async_service_handler, schema=schema
) )

View File

@ -115,6 +115,7 @@ from .const import (
) )
from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity
from .gateway import XiaomiGatewayDevice from .gateway import XiaomiGatewayDevice
from .typing import ServiceMethodDetails
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -176,16 +177,16 @@ SERVICE_SCHEMA_POWER_PRICE = SERVICE_SCHEMA.extend(
) )
SERVICE_TO_METHOD = { SERVICE_TO_METHOD = {
SERVICE_SET_WIFI_LED_ON: {"method": "async_set_wifi_led_on"}, SERVICE_SET_WIFI_LED_ON: ServiceMethodDetails(method="async_set_wifi_led_on"),
SERVICE_SET_WIFI_LED_OFF: {"method": "async_set_wifi_led_off"}, SERVICE_SET_WIFI_LED_OFF: ServiceMethodDetails(method="async_set_wifi_led_off"),
SERVICE_SET_POWER_MODE: { SERVICE_SET_POWER_MODE: ServiceMethodDetails(
"method": "async_set_power_mode", method="async_set_power_mode",
"schema": SERVICE_SCHEMA_POWER_MODE, schema=SERVICE_SCHEMA_POWER_MODE,
}, ),
SERVICE_SET_POWER_PRICE: { SERVICE_SET_POWER_PRICE: ServiceMethodDetails(
"method": "async_set_power_price", method="async_set_power_price",
"schema": SERVICE_SCHEMA_POWER_PRICE, schema=SERVICE_SCHEMA_POWER_PRICE,
}, ),
} }
MODEL_TO_FEATURES_MAP = { MODEL_TO_FEATURES_MAP = {
@ -488,9 +489,9 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities):
update_tasks = [] update_tasks = []
for device in devices: for device in devices:
if not hasattr(device, method["method"]): if not hasattr(device, method.method):
continue continue
await getattr(device, method["method"])(**params) await getattr(device, method.method)(**params)
update_tasks.append( update_tasks.append(
asyncio.create_task(device.async_update_ha_state(True)) asyncio.create_task(device.async_update_ha_state(True))
) )
@ -499,7 +500,7 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities):
await asyncio.wait(update_tasks) await asyncio.wait(update_tasks)
for plug_service, method in SERVICE_TO_METHOD.items(): for plug_service, method in SERVICE_TO_METHOD.items():
schema = method.get("schema", SERVICE_SCHEMA) schema = method.schema or SERVICE_SCHEMA
hass.services.async_register( hass.services.async_register(
DOMAIN, plug_service, async_service_handler, schema=schema DOMAIN, plug_service, async_service_handler, schema=schema
) )

View File

@ -0,0 +1,12 @@
"""Typings for the xiaomi_miio integration."""
from typing import NamedTuple
import voluptuous as vol
class ServiceMethodDetails(NamedTuple):
"""Details for SERVICE_TO_METHOD mapping."""
method: str
schema: vol.Schema | None = None