diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index a338f6cf161..eb77880687d 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -54,7 +54,10 @@ from homeassistant.helpers.script import ( Script, ) from homeassistant.helpers.script_variables import ScriptVariables -from homeassistant.helpers.service import async_register_admin_service +from homeassistant.helpers.service import ( + ReloadServiceHelper, + async_register_admin_service, +) from homeassistant.helpers.trace import ( TraceElement, script_execution_set, @@ -253,8 +256,14 @@ async def async_setup(hass, config): await _async_process_config(hass, conf, component) hass.bus.async_fire(EVENT_AUTOMATION_RELOADED, context=service_call.context) + reload_helper = ReloadServiceHelper(reload_service_handler) + async_register_admin_service( - hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({}) + hass, + DOMAIN, + SERVICE_RELOAD, + reload_helper.execute_service, + schema=vol.Schema({}), ) return True diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 31befb36531..cbbbbc24643 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -783,3 +783,43 @@ def verify_domain_control( return check_permissions return decorator + + +class ReloadServiceHelper: + """Helper for reload services to minimize unnecessary reloads.""" + + def __init__(self, service_func: Callable[[ServiceCall], Awaitable]): + """Initialize ReloadServiceHelper.""" + self._service_func = service_func + self._service_running = False + self._service_condition = asyncio.Condition() + + async def execute_service(self, service_call: ServiceCall) -> None: + """Execute the service. + + If a previous reload task if currently in progress, wait for it to finish first. + Once the previous reload task has finished, one of the waiting tasks will be + assigned to execute the reload, the others will wait for the reload to finish. + """ + + do_reload = False + async with self._service_condition: + if self._service_running: + # A previous reload task is already in progress, wait for it to finish + await self._service_condition.wait() + + async with self._service_condition: + if not self._service_running: + # This task will do the reload + self._service_running = True + do_reload = True + else: + # Another task will perform the reload, wait for it to finish + await self._service_condition.wait() + + if do_reload: + # Reload, then notify other tasks + await self._service_func(service_call) + async with self._service_condition: + self._service_running = False + self._service_condition.notify_all()