Add support for simultaneous runs of Script helper - Part 2 (#32442)
* Add limit parameter to service call methods * Break out prep part of async_call_from_config for use elsewhere * Minor cleanup * Fix improper use of asyncio.wait * Fix state update Call change listener immediately if its a callback * Fix exception handling and logging * Merge Script helper if_running/run_mode parameters into script_mode - Remove background/blocking _ScriptRun subclasses which are no longer needed. * Add queued script mode * Disable timeout when making fully blocking script call * Don't call change listener when restarting script This makes restart mode behavior consistent with parallel & queue modes. * Changes per review - Call all script services (except script.turn_off) with no time limit. - Fix handling of lock in _QueuedScriptRun and add comments to make it clearer how this code works. * Changes per review 2 - Move cancel shielding "up" from _ScriptRun.async_run to Script.async_run (and apply to new style scripts only.) This makes sure Script class also properly handles cancellation which it wasn't doing before. - In _ScriptRun._async_call_service_step, instead of using script.turn_off service, just cancel service call and let it handle the cancellation accordingly. * Fix bugs - Add missing call to change listener in Script.async_run in cancelled path. - Cancel service task if ServiceRegistry.async_call cancelled. * Revert last changes to ServiceRegistry.async_call * Minor Script helper fixes & test improvements - Don't log asyncio.CancelledError exceptions. - Make change_listener a public attribute. - Test overhaul - Parametrize tests. - Use common test functions. - Mock timeout so tests don't need to wait for real time to elapse. - Add common function for waiting for script action step.
This commit is contained in:
parent
da761fdd39
commit
5f5cb8bea8
4 changed files with 948 additions and 1444 deletions
|
@ -56,12 +56,27 @@ async def async_call_from_config(
|
|||
hass, config, blocking=False, variables=None, validate_config=True, context=None
|
||||
):
|
||||
"""Call a service based on a config hash."""
|
||||
try:
|
||||
parms = async_prepare_call_from_config(hass, config, variables, validate_config)
|
||||
except HomeAssistantError as ex:
|
||||
if blocking:
|
||||
raise
|
||||
_LOGGER.error(ex)
|
||||
else:
|
||||
await hass.services.async_call(*parms, blocking, context)
|
||||
|
||||
|
||||
@ha.callback
|
||||
@bind_hass
|
||||
def async_prepare_call_from_config(hass, config, variables=None, validate_config=False):
|
||||
"""Prepare to call a service based on a config hash."""
|
||||
if validate_config:
|
||||
try:
|
||||
config = cv.SERVICE_SCHEMA(config)
|
||||
except vol.Invalid as ex:
|
||||
_LOGGER.error("Invalid config for calling service: %s", ex)
|
||||
return
|
||||
raise HomeAssistantError(
|
||||
f"Invalid config for calling service: {ex}"
|
||||
) from ex
|
||||
|
||||
if CONF_SERVICE in config:
|
||||
domain_service = config[CONF_SERVICE]
|
||||
|
@ -71,17 +86,15 @@ async def async_call_from_config(
|
|||
domain_service = config[CONF_SERVICE_TEMPLATE].async_render(variables)
|
||||
domain_service = cv.service(domain_service)
|
||||
except TemplateError as ex:
|
||||
if blocking:
|
||||
raise
|
||||
_LOGGER.error("Error rendering service name template: %s", ex)
|
||||
return
|
||||
except vol.Invalid:
|
||||
if blocking:
|
||||
raise
|
||||
_LOGGER.error("Template rendered invalid service: %s", domain_service)
|
||||
return
|
||||
raise HomeAssistantError(
|
||||
f"Error rendering service name template: {ex}"
|
||||
) from ex
|
||||
except vol.Invalid as ex:
|
||||
raise HomeAssistantError(
|
||||
f"Template rendered invalid service: {domain_service}"
|
||||
) from ex
|
||||
|
||||
domain, service_name = domain_service.split(".", 1)
|
||||
domain, service = domain_service.split(".", 1)
|
||||
service_data = dict(config.get(CONF_SERVICE_DATA, {}))
|
||||
|
||||
if CONF_SERVICE_DATA_TEMPLATE in config:
|
||||
|
@ -91,15 +104,12 @@ async def async_call_from_config(
|
|||
template.render_complex(config[CONF_SERVICE_DATA_TEMPLATE], variables)
|
||||
)
|
||||
except TemplateError as ex:
|
||||
_LOGGER.error("Error rendering data template: %s", ex)
|
||||
return
|
||||
raise HomeAssistantError(f"Error rendering data template: {ex}") from ex
|
||||
|
||||
if CONF_SERVICE_ENTITY_ID in config:
|
||||
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
|
||||
|
||||
await hass.services.async_call(
|
||||
domain, service_name, service_data, blocking=blocking, context=context
|
||||
)
|
||||
return domain, service, service_data
|
||||
|
||||
|
||||
@bind_hass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue