1 """Offer reusable conditions."""
3 from __future__
import annotations
6 from collections
import deque
7 from collections.abc
import Callable, Container, Generator
8 from contextlib
import contextmanager
9 from datetime
import datetime, time
as dt_time, timedelta
10 import functools
as ft
14 from typing
import Any, Protocol, cast
16 import voluptuous
as vol
52 ConditionErrorContainer,
54 ConditionErrorMessage,
62 from .
import config_validation
as cv, entity_registry
as er
63 from .sun
import get_astral_event_date
64 from .template
import Template, render_complex
75 from .typing
import ConfigType, TemplateVarsType
77 ASYNC_FROM_CONFIG_FORMAT =
"async_{}_from_config"
78 FROM_CONFIG_FORMAT =
"{}_from_config"
79 VALIDATE_CONFIG_FORMAT =
"{}_validate_config"
83 "device":
"device_automation",
85 "numeric_state":
None,
95 INPUT_ENTITY_ID = re.compile(
96 r"^input_(?:select|text|number|boolean|datetime)\.(?!.+__)(?!_)[\da-z_]+(?<!_)$"
101 """Define the format of device_condition modules.
103 Each module must define either CONDITION_SCHEMA or async_validate_condition_config.
106 CONDITION_SCHEMA: vol.Schema
109 self, hass: HomeAssistant, config: ConfigType
111 """Validate config."""
114 self, hass: HomeAssistant, config: ConfigType
115 ) -> ConditionCheckerType:
116 """Evaluate state based on configuration."""
119 type ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], bool |
None]
123 """Append a TraceElement to trace[path]."""
130 """Set the result of TraceElement at the top of the stack."""
131 node = trace_stack_top(trace_stack_cv)
138 node.set_result(result=result, **kwargs)
142 """Update the result of TraceElement at the top of the stack."""
143 node = trace_stack_top(trace_stack_cv)
150 node.update_result(**kwargs)
155 """Trace condition evaluation."""
157 trace_element = trace_stack_top(trace_stack_cv)
158 if trace_element
and trace_element.reuse_by_child:
160 trace_element.reuse_by_child =
False
163 trace_stack_push(trace_stack_cv, trace_element)
166 except Exception
as ex:
167 trace_element.set_error(ex)
175 """Wrap a condition function to enable basic tracing."""
178 def wrapper(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool |
None:
179 """Trace condition."""
181 result = condition(hass, variables)
189 hass: HomeAssistant, config: ConfigType
190 ) -> ConditionProtocol |
None:
191 platform = config[CONF_CONDITION]
192 platform = _PLATFORM_ALIASES.get(platform, platform)
197 except IntegrationNotFound:
199 f
'Invalid condition "{platform}" specified {config}'
202 return await integration.async_get_platform(
"condition")
205 f
"Integration '{platform}' does not provide condition support"
212 ) -> ConditionCheckerType:
213 """Turn a condition configuration into a method.
215 Should be run on the event loop.
221 condition = config.get(CONF_CONDITION)
222 for fmt
in (ASYNC_FROM_CONFIG_FORMAT, FROM_CONFIG_FORMAT):
223 factory = getattr(sys.modules[__name__], fmt.format(condition),
None)
228 factory = platform.async_condition_from_config
231 if CONF_ENABLED
in config:
232 enabled = config[CONF_ENABLED]
233 if isinstance(enabled, Template):
235 enabled = enabled.async_render(limited=
True)
236 except TemplateError
as err:
238 f
"Error rendering condition enabled template: {err}"
242 @trace_condition_function
243 def disabled_condition(
244 hass: HomeAssistant, variables: TemplateVarsType =
None
246 """Condition not enabled, will act as if it didn't exist."""
249 return disabled_condition
252 check_factory = factory
253 while isinstance(check_factory, ft.partial):
254 check_factory = check_factory.func
256 if asyncio.iscoroutinefunction(check_factory):
257 return cast(ConditionCheckerType, await factory(hass, config))
258 return cast(ConditionCheckerType, factory(config))
262 hass: HomeAssistant, config: ConfigType
263 ) -> ConditionCheckerType:
264 """Create multi condition matcher using 'AND'."""
265 checks = [await
async_from_config(hass, entry)
for entry
in config[
"conditions"]]
267 @trace_condition_function
268 def if_and_condition(
269 hass: HomeAssistant, variables: TemplateVarsType =
None
271 """Test and condition."""
273 for index, check
in enumerate(checks):
276 if check(hass, variables)
is False:
278 except ConditionError
as ex:
289 return if_and_condition
293 hass: HomeAssistant, config: ConfigType
294 ) -> ConditionCheckerType:
295 """Create multi condition matcher using 'OR'."""
296 checks = [await
async_from_config(hass, entry)
for entry
in config[
"conditions"]]
298 @trace_condition_function
300 hass: HomeAssistant, variables: TemplateVarsType =
None
302 """Test or condition."""
304 for index, check
in enumerate(checks):
307 if check(hass, variables)
is True:
309 except ConditionError
as ex:
320 return if_or_condition
324 hass: HomeAssistant, config: ConfigType
325 ) -> ConditionCheckerType:
326 """Create multi condition matcher using 'NOT'."""
327 checks = [await
async_from_config(hass, entry)
for entry
in config[
"conditions"]]
329 @trace_condition_function
330 def if_not_condition(
331 hass: HomeAssistant, variables: TemplateVarsType =
None
333 """Test not condition."""
335 for index, check
in enumerate(checks):
338 if check(hass, variables):
340 except ConditionError
as ex:
351 return if_not_condition
356 entity: str | State |
None,
357 below: float | str |
None =
None,
358 above: float | str |
None =
None,
359 value_template: Template |
None =
None,
360 variables: TemplateVarsType =
None,
362 """Test a numeric state condition."""
363 return run_callback_threadsafe(
377 entity: str | State |
None,
378 below: float | str |
None =
None,
379 above: float | str |
None =
None,
380 value_template: Template |
None =
None,
381 variables: TemplateVarsType =
None,
382 attribute: str |
None =
None,
384 """Test a numeric state condition."""
388 if isinstance(entity, str):
391 if (entity := hass.states.get(entity))
is None:
394 entity_id = entity.entity_id
396 if attribute
is not None and attribute
not in entity.attributes:
399 message=f
"attribute '{attribute}' of entity {entity_id} does not exist",
404 if value_template
is None:
405 if attribute
is None:
408 value = entity.attributes.get(attribute)
410 variables =
dict(variables
or {})
411 variables[
"state"] = entity
413 value = value_template.async_render(variables)
414 except TemplateError
as ex:
416 "numeric_state", f
"template error: {ex}"
420 if value
in (
None, STATE_UNAVAILABLE, STATE_UNKNOWN):
423 message=f
"value '{value}' is non-numeric and treated as False",
428 fvalue =
float(value)
429 except (ValueError, TypeError)
as ex:
432 f
"entity {entity_id} state '{value}' cannot be processed as a number",
435 if below
is not None:
436 if isinstance(below, str):
437 if not (below_entity := hass.states.get(below)):
439 "numeric_state", f
"unknown 'below' entity {below}"
441 if below_entity.state
in (
447 if fvalue >=
float(below_entity.state):
451 wanted_state_below=
float(below_entity.state),
454 except (ValueError, TypeError)
as ex:
458 f
"the 'below' entity {below} state '{below_entity.state}'"
459 " cannot be processed as a number"
462 elif fvalue >= below:
466 if above
is not None:
467 if isinstance(above, str):
468 if not (above_entity := hass.states.get(above)):
470 "numeric_state", f
"unknown 'above' entity {above}"
472 if above_entity.state
in (
478 if fvalue <=
float(above_entity.state):
482 wanted_state_above=
float(above_entity.state),
485 except (ValueError, TypeError)
as ex:
489 f
"the 'above' entity {above} state '{above_entity.state}'"
490 " cannot be processed as a number"
493 elif fvalue <= above:
502 """Wrap action method with state based condition."""
503 entity_ids = config.get(CONF_ENTITY_ID, [])
504 attribute = config.get(CONF_ATTRIBUTE)
505 below = config.get(CONF_BELOW)
506 above = config.get(CONF_ABOVE)
507 value_template = config.get(CONF_VALUE_TEMPLATE)
509 @trace_condition_function
510 def if_numeric_state(
511 hass: HomeAssistant, variables: TemplateVarsType =
None
513 """Test numeric state condition."""
515 for index, entity_id
in enumerate(entity_ids):
528 except ConditionError
as ex:
531 "numeric_state", index=index, total=len(entity_ids), error=ex
541 return if_numeric_state
546 entity: str | State |
None,
548 for_period: timedelta |
None =
None,
549 attribute: str |
None =
None,
550 variables: TemplateVarsType =
None,
552 """Test if state matches requirements.
559 if isinstance(entity, str):
562 if (entity := hass.states.get(entity))
is None:
565 entity_id = entity.entity_id
567 if attribute
is not None and attribute
not in entity.attributes:
570 message=f
"attribute '{attribute}' of entity {entity_id} does not exist",
574 assert isinstance(entity, State)
576 if attribute
is None:
577 value: Any = entity.state
579 value = entity.attributes.get(attribute)
581 if not isinstance(req_state, list):
582 req_state = [req_state]
585 for req_state_value
in req_state:
586 state_value = req_state_value
588 isinstance(req_state_value, str)
589 and INPUT_ENTITY_ID.match(req_state_value)
is not None
591 if not (state_entity := hass.states.get(req_state_value)):
593 "state", f
"the 'state' entity {req_state_value} is unavailable"
595 state_value = state_entity.state
596 is_state = value == state_value
600 if for_period
is None or not is_state:
605 for_period = cv.positive_time_period(
render_complex(for_period, variables))
606 except TemplateError
as ex:
608 except vol.Invalid
as ex:
611 duration = dt_util.utcnow() - cast(timedelta, for_period)
612 duration_ok = duration > entity.last_changed
618 """Wrap action method with state based condition."""
619 entity_ids = config.get(CONF_ENTITY_ID, [])
620 req_states: str | list[str] = config.get(CONF_STATE, [])
621 for_period = config.get(CONF_FOR)
622 attribute = config.get(CONF_ATTRIBUTE)
623 match = config.get(CONF_MATCH, ENTITY_MATCH_ALL)
625 if not isinstance(req_states, list):
626 req_states = [req_states]
628 @trace_condition_function
629 def if_state(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
630 """Test if condition."""
632 result: bool = match != ENTITY_MATCH_ANY
633 for index, entity_id
in enumerate(entity_ids):
637 hass, entity_id, req_states, for_period, attribute, variables
640 elif match == ENTITY_MATCH_ALL:
642 except ConditionError
as ex:
645 "state", index=index, total=len(entity_ids), error=ex
660 before: str |
None =
None,
661 after: str |
None =
None,
662 before_offset: timedelta |
None =
None,
663 after_offset: timedelta |
None =
None,
665 """Test if current time matches sun requirements."""
666 utcnow = dt_util.utcnow()
667 today = dt_util.as_local(utcnow).
date()
668 before_offset = before_offset
or timedelta(0)
669 after_offset = after_offset
or timedelta(0)
674 has_sunrise_condition = SUN_EVENT_SUNRISE
in (before, after)
675 has_sunset_condition = SUN_EVENT_SUNSET
in (before, after)
677 after_sunrise = today > dt_util.as_local(cast(datetime, sunrise)).
date()
678 if after_sunrise
and has_sunrise_condition:
682 after_sunset = today > dt_util.as_local(cast(datetime, sunset)).
date()
683 if after_sunset
and has_sunset_condition:
698 if before == SUN_EVENT_SUNRISE
and after == SUN_EVENT_SUNSET:
699 wanted_time_before = cast(datetime, sunrise) + before_offset
701 wanted_time_after = cast(datetime, sunset) + after_offset
703 return utcnow < wanted_time_before
or utcnow > wanted_time_after
705 if sunrise
is None and has_sunrise_condition:
710 if sunset
is None and has_sunset_condition:
715 if before == SUN_EVENT_SUNRISE:
716 wanted_time_before = cast(datetime, sunrise) + before_offset
718 if utcnow > wanted_time_before:
721 if before == SUN_EVENT_SUNSET:
722 wanted_time_before = cast(datetime, sunset) + before_offset
724 if utcnow > wanted_time_before:
727 if after == SUN_EVENT_SUNRISE:
728 wanted_time_after = cast(datetime, sunrise) + after_offset
730 if utcnow < wanted_time_after:
733 if after == SUN_EVENT_SUNSET:
734 wanted_time_after = cast(datetime, sunset) + after_offset
736 if utcnow < wanted_time_after:
743 """Wrap action method with sun based condition."""
744 before = config.get(
"before")
745 after = config.get(
"after")
746 before_offset = config.get(
"before_offset")
747 after_offset = config.get(
"after_offset")
749 @trace_condition_function
750 def sun_if(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
751 """Validate time based if-condition."""
752 return sun(hass, before, after, before_offset, after_offset)
758 hass: HomeAssistant, value_template: Template, variables: TemplateVarsType =
None
760 """Test if template condition matches."""
761 return run_callback_threadsafe(
762 hass.loop, async_template, hass, value_template, variables
768 value_template: Template,
769 variables: TemplateVarsType =
None,
770 trace_result: bool =
True,
772 """Test if template condition matches."""
774 info = value_template.async_render_to_info(variables, parse_result=
False)
775 value = info.result()
776 except TemplateError
as ex:
779 result = value.lower() ==
"true"
786 """Wrap action method with state based condition."""
787 value_template = cast(Template, config.get(CONF_VALUE_TEMPLATE))
789 @trace_condition_function
790 def template_if(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
791 """Validate template based if-condition."""
799 before: dt_time | str |
None =
None,
800 after: dt_time | str |
None =
None,
801 weekday: str | Container[str] |
None =
None,
803 """Test if local time condition matches.
805 Handle the fact that time is continuous and we may be testing for
806 a period that crosses midnight. In that case it is easier to test
807 for the opposite. "(23:59 <= now < 00:01)" would be the same as
808 "not (00:01 <= now < 23:59)".
811 now_time = now.time()
815 elif isinstance(after, str):
816 if not (after_entity := hass.states.get(after)):
818 if after_entity.domain ==
"input_datetime":
820 after_entity.attributes.get(
"hour", 23),
821 after_entity.attributes.get(
"minute", 59),
822 after_entity.attributes.get(
"second", 59),
824 elif after_entity.domain ==
"time" and after_entity.state
not in (
828 after = datetime.strptime(after_entity.state,
"%H:%M:%S").
time()
830 after_entity.attributes.get(ATTR_DEVICE_CLASS)
831 == SensorDeviceClass.TIMESTAMP
832 )
and after_entity.state
not in (
836 after_datetime = dt_util.parse_datetime(after_entity.state)
837 if after_datetime
is None:
839 after = dt_util.as_local(after_datetime).
time()
844 before = dt_time(23, 59, 59, 999999)
845 elif isinstance(before, str):
846 if not (before_entity := hass.states.get(before)):
848 if before_entity.domain ==
"input_datetime":
850 before_entity.attributes.get(
"hour", 23),
851 before_entity.attributes.get(
"minute", 59),
852 before_entity.attributes.get(
"second", 59),
854 elif before_entity.domain ==
"time":
856 before = datetime.strptime(before_entity.state,
"%H:%M:%S").
time()
860 before_entity.attributes.get(ATTR_DEVICE_CLASS)
861 == SensorDeviceClass.TIMESTAMP
862 )
and before_entity.state
not in (
866 before_timedatime = dt_util.parse_datetime(before_entity.state)
867 if before_timedatime
is None:
869 before = dt_util.as_local(before_timedatime).
time()
875 if not after <= now_time < before:
879 if before <= now_time < after:
882 if weekday
is not None:
883 now_weekday = WEEKDAYS[now.weekday()]
887 isinstance(weekday, str)
888 and weekday != now_weekday
889 or now_weekday
not in weekday
897 """Wrap action method with time based condition."""
898 before = config.get(CONF_BEFORE)
899 after = config.get(CONF_AFTER)
900 weekday = config.get(CONF_WEEKDAY)
902 @trace_condition_function
903 def time_if(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
904 """Validate time based if-condition."""
905 return time(hass, before, after, weekday)
912 zone_ent: str | State |
None,
913 entity: str | State |
None,
915 """Test if zone-condition matches.
922 if isinstance(zone_ent, str):
923 zone_ent_id = zone_ent
925 if (zone_ent := hass.states.get(zone_ent))
is None:
931 if isinstance(entity, str):
934 if (entity := hass.states.get(entity))
is None:
937 entity_id = entity.entity_id
945 latitude = entity.attributes.get(ATTR_LATITUDE)
946 longitude = entity.attributes.get(ATTR_LONGITUDE)
950 "zone", f
"entity {entity_id} has no 'latitude' attribute"
953 if longitude
is None:
955 "zone", f
"entity {entity_id} has no 'longitude' attribute"
958 return zone_cmp.in_zone(
959 zone_ent, latitude, longitude, entity.attributes.get(ATTR_GPS_ACCURACY, 0)
964 """Wrap action method with zone based condition."""
965 entity_ids = config.get(CONF_ENTITY_ID, [])
966 zone_entity_ids = config.get(CONF_ZONE, [])
968 @trace_condition_function
969 def if_in_zone(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
970 """Test if condition."""
974 for entity_id
in entity_ids:
976 for zone_entity_id
in zone_entity_ids:
978 if zone(hass, zone_entity_id, entity_id):
980 except ConditionErrorMessage
as ex:
985 f
"error matching {entity_id} with {zone_entity_id}:"
995 if errors
and not all_ok:
1004 hass: HomeAssistant, config: ConfigType
1005 ) -> ConditionCheckerType:
1006 """Test a trigger condition."""
1007 trigger_id = config[CONF_ID]
1009 @trace_condition_function
1010 def trigger_if(hass: HomeAssistant, variables: TemplateVarsType =
None) -> bool:
1011 """Validate trigger based if-condition."""
1013 variables
is not None
1014 and "trigger" in variables
1015 and variables[
"trigger"].
get(
"id")
in trigger_id
1022 hass: HomeAssistant, config: ConfigType
1024 """Validate numeric_state condition config."""
1026 registry = er.async_get(hass)
1027 config =
dict(config)
1028 config[CONF_ENTITY_ID] = er.async_validate_entity_ids(
1029 registry, cv.entity_ids_or_uuids(config[CONF_ENTITY_ID])
1035 """Validate state condition config."""
1037 registry = er.async_get(hass)
1038 config =
dict(config)
1039 config[CONF_ENTITY_ID] = er.async_validate_entity_ids(
1040 registry, cv.entity_ids_or_uuids(config[CONF_ENTITY_ID])
1046 hass: HomeAssistant, config: ConfigType
1048 """Validate config."""
1049 condition = config[CONF_CONDITION]
1050 if condition
in (
"and",
"not",
"or"):
1052 for sub_cond
in config[
"conditions"]:
1054 conditions.append(sub_cond)
1055 config[
"conditions"] = conditions
1059 if platform
is not None and hasattr(platform,
"async_validate_condition_config"):
1060 return await platform.async_validate_condition_config(hass, config)
1061 if platform
is None and condition
in (
"numeric_state",
"state"):
1063 Callable[[HomeAssistant, ConfigType], ConfigType],
1064 getattr(sys.modules[__name__], VALIDATE_CONFIG_FORMAT.format(condition)),
1066 return validator(hass, config)
1072 hass: HomeAssistant, conditions: list[ConfigType]
1073 ) -> list[ConfigType | Template]:
1074 """Validate config."""
1081 hass: HomeAssistant,
1082 condition_configs: list[ConfigType],
1083 logger: logging.Logger,
1085 ) -> Callable[[TemplateVarsType], bool]:
1086 """AND all conditions."""
1087 checks: list[ConditionCheckerType] = [
1089 for condition_config
in condition_configs
1092 def check_conditions(variables: TemplateVarsType =
None) -> bool:
1093 """AND all conditions."""
1094 errors: list[ConditionErrorIndex] = []
1095 for index, check
in enumerate(checks):
1098 if check(hass, variables)
is False:
1100 except ConditionError
as ex:
1103 "condition", index=index, total=len(checks), error=ex
1109 "Error evaluating condition in '%s':\n%s",
1117 return check_conditions
1122 """Extract entities from a condition."""
1123 referenced: set[str] = set()
1124 to_process = deque([config])
1127 config = to_process.popleft()
1128 if isinstance(config, Template):
1131 condition = config[CONF_CONDITION]
1133 if condition
in (
"and",
"not",
"or"):
1134 to_process.extend(config[
"conditions"])
1137 entity_ids = config.get(CONF_ENTITY_ID)
1139 if isinstance(entity_ids, str):
1140 entity_ids = [entity_ids]
1142 if entity_ids
is not None:
1143 referenced.update(entity_ids)
1150 """Extract devices from a condition."""
1152 to_process = deque([config])
1155 config = to_process.popleft()
1156 if isinstance(config, Template):
1159 condition = config[CONF_CONDITION]
1161 if condition
in (
"and",
"not",
"or"):
1162 to_process.extend(config[
"conditions"])
1165 if condition !=
"device":
1168 if (device_id := config.get(CONF_DEVICE_ID))
is not None:
1169 referenced.add(device_id)
ConfigType async_validate_condition_config(self, HomeAssistant hass, ConfigType config)
web.Response get(self, web.Request request, str config_key)
condition.ConditionCheckerType async_condition_from_config(HomeAssistant hass, ConfigType config)
bool zone(HomeAssistant hass, str|State|None zone_ent, str|State|None entity)
bool state(HomeAssistant hass, str|State|None entity, Any req_state, timedelta|None for_period=None, str|None attribute=None, TemplateVarsType variables=None)
None condition_trace_set_result(bool result, **Any kwargs)
ConditionProtocol|None _async_get_condition_platform(HomeAssistant hass, ConfigType config)
ConditionCheckerType async_and_from_config(HomeAssistant hass, ConfigType config)
Callable[[TemplateVarsType], bool] async_conditions_from_config(HomeAssistant hass, list[ConfigType] condition_configs, logging.Logger logger, str name)
bool numeric_state(HomeAssistant hass, str|State|None entity, float|str|None below=None, float|str|None above=None, Template|None value_template=None, TemplateVarsType variables=None)
ConditionCheckerType async_not_from_config(HomeAssistant hass, ConfigType config)
None condition_trace_update_result(**Any kwargs)
ConditionCheckerType state_from_config(ConfigType config)
bool template(HomeAssistant hass, Template value_template, TemplateVarsType variables=None)
set[str] async_extract_devices(ConfigType|Template config)
ConditionCheckerType async_from_config(HomeAssistant hass, ConfigType config)
bool async_numeric_state(HomeAssistant hass, str|State|None entity, float|str|None below=None, float|str|None above=None, Template|None value_template=None, TemplateVarsType variables=None, str|None attribute=None)
ConditionCheckerType time_from_config(ConfigType config)
bool time(HomeAssistant hass, dt_time|str|None before=None, dt_time|str|None after=None, str|Container[str]|None weekday=None)
ConditionCheckerType trace_condition_function(ConditionCheckerType condition)
ConditionCheckerType async_or_from_config(HomeAssistant hass, ConfigType config)
list[ConfigType|Template] async_validate_conditions_config(HomeAssistant hass, list[ConfigType] conditions)
set[str] async_extract_entities(ConfigType|Template config)
ConfigType async_validate_condition_config(HomeAssistant hass, ConfigType config)
ConditionCheckerType sun_from_config(ConfigType config)
ConditionCheckerType async_template_from_config(ConfigType config)
TraceElement condition_trace_append(TemplateVarsType variables, str path)
ConfigType state_validate_config(HomeAssistant hass, ConfigType config)
bool async_template(HomeAssistant hass, Template value_template, TemplateVarsType variables=None, bool trace_result=True)
Generator[TraceElement] trace_condition(TemplateVarsType variables)
ConditionCheckerType async_numeric_state_from_config(ConfigType config)
ConditionCheckerType zone_from_config(ConfigType config)
ConfigType numeric_state_validate_config(HomeAssistant hass, ConfigType config)
bool sun(HomeAssistant hass, str|None before=None, str|None after=None, timedelta|None before_offset=None, timedelta|None after_offset=None)
ConditionCheckerType async_trigger_from_config(HomeAssistant hass, ConfigType config)
datetime.datetime|None get_astral_event_date(HomeAssistant hass, str event, datetime.date|datetime.datetime|None date=None)
Any render_complex(Any value, TemplateVarsType variables=None, bool limited=False, bool parse_result=True)
None trace_stack_pop(ContextVar[list[Any]|None] trace_stack_var)
Generator[None] trace_path(str|list[str] suffix)
None trace_append_element(TraceElement trace_element, int|None maxlen=None)
Integration async_get_integration(HomeAssistant hass, str domain)
def check(config_dir, secrets=False)