From 38210ebbc6a06220b3be3a29259e13683b693f4e Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Tue, 30 Jun 2020 12:22:26 -0500 Subject: [PATCH] Enhance script integration to use new features in script helper (#37201) --- homeassistant/components/script/__init__.py | 143 ++++++++++++--- tests/components/script/test_init.py | 188 +++++++++++++++----- 2 files changed, 266 insertions(+), 65 deletions(-) diff --git a/homeassistant/components/script/__init__.py b/homeassistant/components/script/__init__.py index e80dcfa8027..740a5a21a5f 100644 --- a/homeassistant/components/script/__init__.py +++ b/homeassistant/components/script/__init__.py @@ -10,6 +10,7 @@ from homeassistant.const import ( ATTR_NAME, CONF_ALIAS, CONF_ICON, + CONF_MODE, SERVICE_RELOAD, SERVICE_TOGGLE, SERVICE_TURN_OFF, @@ -21,7 +22,13 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import make_entity_service_schema from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.script import Script +from homeassistant.helpers.script import ( + DEFAULT_QUEUE_MAX, + SCRIPT_MODE_CHOICES, + SCRIPT_MODE_LEGACY, + SCRIPT_MODE_QUEUE, + Script, +) from homeassistant.helpers.service import async_set_service_schema from homeassistant.loader import bind_hass @@ -37,11 +44,47 @@ CONF_DESCRIPTION = "description" CONF_EXAMPLE = "example" CONF_FIELDS = "fields" CONF_SEQUENCE = "sequence" +CONF_QUEUE_MAX = "queue_size" ENTITY_ID_FORMAT = DOMAIN + ".{}" EVENT_SCRIPT_STARTED = "script_started" + +def _deprecated_legacy_mode(config): + legacy_scripts = [] + for object_id, cfg in config.items(): + mode = cfg.get(CONF_MODE) + if mode is None: + legacy_scripts.append(object_id) + cfg[CONF_MODE] = SCRIPT_MODE_LEGACY + if legacy_scripts: + _LOGGER.warning( + "Script behavior has changed. " + "To continue using previous behavior, which is now deprecated, " + "add '%s: %s' to script(s): %s.", + CONF_MODE, + SCRIPT_MODE_LEGACY, + ", ".join(legacy_scripts), + ) + return config + + +def _queue_max(config): + for object_id, cfg in config.items(): + mode = cfg[CONF_MODE] + queue_max = cfg.get(CONF_QUEUE_MAX) + if mode == SCRIPT_MODE_QUEUE: + if queue_max is None: + cfg[CONF_QUEUE_MAX] = DEFAULT_QUEUE_MAX + elif queue_max is not None: + raise vol.Invalid( + f"{CONF_QUEUE_MAX} not valid with {mode} {CONF_MODE} " + f"for script '{object_id}'" + ) + return config + + SCRIPT_ENTRY_SCHEMA = vol.Schema( { vol.Optional(CONF_ALIAS): cv.string, @@ -54,11 +97,20 @@ SCRIPT_ENTRY_SCHEMA = vol.Schema( vol.Optional(CONF_EXAMPLE): cv.string, } }, + vol.Optional(CONF_MODE): vol.In(SCRIPT_MODE_CHOICES), + vol.Optional(CONF_QUEUE_MAX): vol.All(vol.Coerce(int), vol.Range(min=2)), } ) CONFIG_SCHEMA = vol.Schema( - {DOMAIN: cv.schema_with_slug_keys(SCRIPT_ENTRY_SCHEMA)}, extra=vol.ALLOW_EXTRA + { + DOMAIN: vol.All( + cv.schema_with_slug_keys(SCRIPT_ENTRY_SCHEMA), + _deprecated_legacy_mode, + _queue_max, + ) + }, + extra=vol.ALLOW_EXTRA, ) SCRIPT_SERVICE_SCHEMA = vol.Schema(dict) @@ -91,7 +143,7 @@ def scripts_with_entity(hass: HomeAssistant, entity_id: str) -> List[str]: @callback def entities_in_script(hass: HomeAssistant, entity_id: str) -> List[str]: - """Return all entities in a scene.""" + """Return all entities in script.""" if DOMAIN not in hass.data: return [] @@ -122,7 +174,7 @@ def scripts_with_device(hass: HomeAssistant, device_id: str) -> List[str]: @callback def devices_in_script(hass: HomeAssistant, entity_id: str) -> List[str]: - """Return all devices in a scene.""" + """Return all devices in script.""" if DOMAIN not in hass.data: return [] @@ -152,13 +204,16 @@ async def async_setup(hass, config): async def turn_on_service(service): """Call a service to turn script on.""" - # We could turn on script directly here, but we only want to offer - # one way to do it. Otherwise no easy way to detect invocations. - var = service.data.get(ATTR_VARIABLES) - for script in await component.async_extract_from_service(service): - await hass.services.async_call( - DOMAIN, script.object_id, var, context=service.context - ) + variables = service.data.get(ATTR_VARIABLES) + for script_entity in await component.async_extract_from_service(service): + if script_entity.script.is_legacy: + await hass.services.async_call( + DOMAIN, script_entity.object_id, variables, context=service.context + ) + else: + await script_entity.async_turn_on( + variables=variables, context=service.context, wait=False + ) async def turn_off_service(service): """Cancel a script.""" @@ -172,8 +227,8 @@ async def async_setup(hass, config): async def toggle_service(service): """Toggle a script.""" - for script in await component.async_extract_from_service(service): - await script.async_toggle(context=service.context) + for script_entity in await component.async_extract_from_service(service): + await script_entity.async_toggle(context=service.context) hass.services.async_register( DOMAIN, SERVICE_RELOAD, reload_service, schema=RELOAD_SERVICE_SCHEMA @@ -197,24 +252,40 @@ async def _async_process_config(hass, config, component): async def service_handler(service): """Execute a service call to script.