1 """Template helper methods for rendering strings with Home Assistant data."""
3 from __future__
import annotations
5 from ast
import literal_eval
9 from collections.abc
import Callable, Generator, Iterable
10 from contextlib
import AbstractContextManager
11 from contextvars
import ContextVar
12 from copy
import deepcopy
13 from datetime
import date, datetime, time, timedelta
14 from functools
import cache, lru_cache, partial, wraps
18 from operator
import contains
23 from struct
import error
as StructError, pack, unpack_from
25 from types
import CodeType, TracebackType
26 from typing
import Any, Concatenate, Literal, NoReturn, Self, cast, overload
27 from urllib.parse
import urlencode
as urllib_urlencode
30 from awesomeversion
import AwesomeVersion
32 from jinja2
import pass_context, pass_environment, pass_eval_context
33 from jinja2.runtime
import AsyncLoopContext, LoopContext
34 from jinja2.sandbox
import ImmutableSandboxedEnvironment
35 from jinja2.utils
import Namespace
38 from propcache
import under_cached_property
39 import voluptuous
as vol
46 ATTR_UNIT_OF_MEASUREMENT,
47 EVENT_HOMEASSISTANT_START,
48 EVENT_HOMEASSISTANT_STOP,
69 slugify
as slugify_util,
84 location
as loc_helper,
86 from .deprecation
import deprecated_function
87 from .singleton
import singleton
88 from .translation
import async_translate_state
89 from .typing
import TemplateVarsType
93 _LOGGER = logging.getLogger(__name__)
95 DATE_STR_FORMAT =
"%Y-%m-%d %H:%M:%S"
97 _ENVIRONMENT: HassKey[TemplateEnvironment] =
HassKey(
"template.environment")
98 _ENVIRONMENT_LIMITED: HassKey[TemplateEnvironment] =
HassKey(
99 "template.environment_limited"
101 _ENVIRONMENT_STRICT: HassKey[TemplateEnvironment] =
HassKey(
102 "template.environment_strict"
104 _HASS_LOADER =
"template.hass_loader"
107 _IS_NUMERIC = re.compile(
r"^[+-]?(?!0\d)\d*(?:\.\d*)?$")
111 "evalcontextfunction",
112 "environmentfunction",
116 _COLLECTABLE_STATE_ATTRIBUTES = {
127 ALL_STATES_RATE_LIMIT = 60
128 DOMAIN_STATES_RATE_LIMIT = 1
130 _render_info: ContextVar[RenderInfo |
None] = ContextVar(
"_render_info", default=
None)
133 template_cv: ContextVar[tuple[str, str] |
None] = ContextVar(
134 "template_cv", default=
None
152 CACHED_TEMPLATE_STATES = 512
153 EVAL_CACHE_SIZE = 512
155 MAX_CUSTOM_TEMPLATE_SIZE = 5 * 1024 * 1024
156 MAX_TEMPLATE_OUTPUT = 256 * 1024
158 CACHED_TEMPLATE_LRU: LRU[State, TemplateState] = LRU(CACHED_TEMPLATE_STATES)
159 CACHED_TEMPLATE_NO_COLLECT_LRU: LRU[State, TemplateState] = LRU(CACHED_TEMPLATE_STATES)
160 ENTITY_COUNT_GROWTH_FACTOR = 1.2
162 ORJSON_PASSTHROUGH_OPTIONS = (
163 orjson.OPT_PASSTHROUGH_DATACLASS | orjson.OPT_PASSTHROUGH_DATETIME
168 """Return a TemplateState for a state without collecting."""
169 if template_state := CACHED_TEMPLATE_NO_COLLECT_LRU.get(state):
170 return template_state
172 CACHED_TEMPLATE_NO_COLLECT_LRU[state] = template_state
173 return template_state
177 """Return a TemplateState for a state that collects."""
178 if template_state := CACHED_TEMPLATE_LRU.get(state):
179 return template_state
181 CACHED_TEMPLATE_LRU[state] = template_state
182 return template_state
186 """Set up tracking the template LRUs."""
189 def _async_adjust_lru_sizes(_: Any) ->
None:
190 """Adjust the lru cache sizes."""
192 round(hass.states.async_entity_ids_count() * ENTITY_COUNT_GROWTH_FACTOR)
194 for lru
in (CACHED_TEMPLATE_LRU, CACHED_TEMPLATE_NO_COLLECT_LRU):
196 current_size = lru.get_size()
197 if new_size > current_size:
198 lru.set_size(new_size)
201 async_track_time_interval,
205 hass, _async_adjust_lru_sizes,
timedelta(minutes=10)
207 hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_adjust_lru_sizes)
208 hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, callback(
lambda _: cancel()))
213 @deprecated_function(
"automatic setting of Template.hass introduced by HA Core PR #89242",
breaks_in_ha_version="2025.10",
)
214 def attach(hass: HomeAssistant, obj: Any) ->
None:
215 """Recursively attach hass to all template instances in list and dict."""
219 def _attach(hass: HomeAssistant, obj: Any) ->
None:
220 """Recursively attach hass to all template instances in list and dict."""
221 if isinstance(obj, list):
224 elif isinstance(obj, collections.abc.Mapping):
225 for child_key, child_value
in obj.items():
228 elif isinstance(obj, Template):
234 variables: TemplateVarsType =
None,
235 limited: bool =
False,
236 parse_result: bool =
True,
238 """Recursive template creator helper function."""
239 if isinstance(value, list):
241 render_complex(item, variables, limited, parse_result)
for item
in value
243 if isinstance(value, collections.abc.Mapping):
246 item, variables, limited, parse_result
248 for key, item
in value.items()
250 if isinstance(value, Template):
251 return value.async_render(variables, limited=limited, parse_result=parse_result)
257 """Test if data structure is a complex template."""
258 if isinstance(value, Template):
260 if isinstance(value, list):
262 if isinstance(value, collections.abc.Mapping):
263 return any(
is_complex(val)
for val
in value)
or any(
270 """Check if the input is a Jinja2 template."""
271 return "{" in maybe_template
and (
272 "{%" in maybe_template
or "{{" in maybe_template
or "{#" in maybe_template
277 """Result wrapper class to store render result."""
279 render_result: str |
None
283 """Generate a result wrapper."""
286 """Wrapper of a kls that can store render_result."""
288 def __init__(self, *args: Any, render_result: str |
None =
None) ->
None:
290 self.render_result = render_result
292 def __str__(self) -> str:
293 if self.render_result
is None:
296 return str(set(self))
298 return kls.__str__(self)
300 return self.render_result
312 def __new__(cls, value: tuple, *, render_result: str |
None =
None) -> Self:
313 """Create a new tuple class."""
316 def __init__(self, value: tuple, *, render_result: str |
None =
None) ->
None:
317 """Initialize a new tuple class."""
321 """Return string representation."""
328 _types: tuple[type[dict | list | set], ...] = (dict, list, set)
330 RESULT_WRAPPERS[tuple] = TupleWrapper
333 def _true(arg: str) -> bool:
337 def _false(arg: str) -> bool:
341 @lru_cache(maxsize=EVAL_CACHE_SIZE)
343 """Parse a result and cache the result."""
344 result = literal_eval(render_result)
345 if type(result)
in RESULT_WRAPPERS:
346 result = RESULT_WRAPPERS[type(result)](result, render_result=render_result)
355 not isinstance(result, (str, complex))
358 not isinstance(result, (int, float))
360 or isinstance(result, bool)
362 or _IS_NUMERIC.match(render_result)
is not None
371 """Holds information about a template render."""
381 "all_states_lifecycle",
389 def __init__(self, template: Template) ->
None:
394 self.
filterfilter: Callable[[str], bool] = _true
395 self._result: str |
None =
None
397 self.exception: TemplateError |
None =
None
400 self.
domainsdomains: collections.abc.Set[str] = set()
402 self.
entitiesentities: collections.abc.Set[str] = set()
403 self.
rate_limitrate_limit: float |
None =
None
407 """Representation of RenderInfo."""
409 f
"<RenderInfo {self.template}"
410 f
" all_states={self.all_states}"
411 f
" all_states_lifecycle={self.all_states_lifecycle}"
412 f
" domains={self.domains}"
413 f
" domains_lifecycle={self.domains_lifecycle}"
414 f
" entities={self.entities}"
415 f
" rate_limit={self.rate_limit}"
416 f
" has_time={self.has_time}"
417 f
" exception={self.exception}"
418 f
" is_static={self.is_static}"
423 """Template should re-render if the entity state changes.
425 Only when we match specific domains or entities.
432 """Template should re-render if the entity state changes.
434 Only when we match specific entities.
436 return entity_id
in self.
entitiesentities
439 """Template should re-render if the entity is added or removed.
441 Only with domains watched.
446 """Results of the template computation."""
447 if self.exception
is not None:
449 return cast(str, self._result)
465 if self.
all_statesall_states
or self.exception:
466 self.
rate_limitrate_limit = ALL_STATES_RATE_LIMIT
468 self.
rate_limitrate_limit = DOMAIN_STATES_RATE_LIMIT
487 self.
filterfilter = _false
491 """Class to hold a template and manage caching and rendering."""
508 def __init__(self, template: str, hass: HomeAssistant |
None =
None) ->
None:
509 """Instantiate a template.
511 Note: A valid hass instance should always be passed in. The hass parameter
512 will be non optional in Home Assistant Core 2025.10.
515 from .frame
import ReportBehavior, report_usage
517 if not isinstance(template, str):
518 raise TypeError(
"Expected template to be a string")
522 "creates a template object without passing hass",
523 core_behavior=ReportBehavior.LOG,
524 breaks_in_ha_version=
"2025.10",
527 self.
templatetemplate: str = template.strip()
529 self.
_compiled_compiled: jinja2.Template |
None =
None
532 self.
_exc_info_exc_info: sys._OptExcInfo |
None =
None
535 self.
_log_fn_log_fn: Callable[[int, str],
None] |
None =
None
536 self._hash_cache: int = hash(self.
templatetemplate)
537 self._renders: int = 0
540 def _env(self) -> TemplateEnvironment:
541 if self.
hasshass
is None:
544 if self.
_log_fn_log_fn
is not None:
549 wanted_env = _ENVIRONMENT_LIMITED
551 wanted_env = _ENVIRONMENT_STRICT
553 wanted_env = _ENVIRONMENT
554 if (ret := self.
hasshass.data.get(wanted_env))
is None:
561 """Return if template is valid."""
565 if compiled := self.
_env_env.template_cache.get(self.
templatetemplate):
569 with _template_context_manager
as cm:
570 cm.set_template(self.
templatetemplate,
"compiling")
573 except jinja2.TemplateError
as err:
578 variables: TemplateVarsType =
None,
579 parse_result: bool =
True,
580 limited: bool =
False,
583 """Render given template.
585 If limited is True, the template is not allowed to access any function
586 or filter depending on hass or the state machine.
589 if not parse_result
or self.
hasshass
and self.
hasshass.config.legacy_templates:
592 assert self.
hasshass
is not None,
"hass variable not set on template"
593 return run_callback_threadsafe(
595 partial(self.
async_renderasync_render, variables, parse_result, limited, **kwargs),
601 variables: TemplateVarsType =
None,
602 parse_result: bool =
True,
603 limited: bool =
False,
604 strict: bool =
False,
605 log_fn: Callable[[int, str],
None] |
None =
None,
608 """Render given template.
610 This method must be run in the event loop.
612 If limited is True, the template is not allowed to access any function
613 or filter depending on hass or the state machine.
618 if not parse_result
or self.
hasshass
and self.
hasshass.config.legacy_templates:
624 if variables
is not None:
625 kwargs.update(variables)
629 except Exception
as err:
632 if len(render_result) > MAX_TEMPLATE_OUTPUT:
634 f
"Template output exceeded maximum size of {MAX_TEMPLATE_OUTPUT} characters"
637 render_result = render_result.strip()
639 if not parse_result
or self.
hasshass
and self.
hasshass.config.legacy_templates:
645 """Parse the result."""
648 except (ValueError, TypeError, SyntaxError, MemoryError):
656 variables: TemplateVarsType =
None,
657 strict: bool =
False,
658 log_fn: Callable[[int, str],
None] |
None =
None,
661 """Check to see if rendering a template will timeout during render.
663 This is intended to check for expensive templates
664 that will make the system unstable. The template
665 is rendered in the executor to ensure it does not
666 tie up the event loop.
668 This function is not a security control and is only
669 intended to be used as a safety check when testing
672 This method must be run in the event loop.
681 if variables
is not None:
682 kwargs.update(variables)
685 finish_event = asyncio.Event()
687 def _render_template() -> None:
688 assert self.
hasshass
is not None,
"hass variable not set on template"
696 self.
hasshass.loop.call_soon_threadsafe(finish_event.set)
700 template_render_thread.start()
701 async
with asyncio.timeout(timeout):
702 await finish_event.wait()
706 template_render_thread.raise_exc(TimeoutError)
709 template_render_thread.join()
716 variables: TemplateVarsType =
None,
717 strict: bool =
False,
718 log_fn: Callable[[int, str],
None] |
None =
None,
721 """Render the template and collect an entity filter."""
722 if self.
hasshass
and self.
hasshass.config.debug:
723 self.
hasshass.verify_event_loop_thread(
"async_render_to_info")
728 if not self.
hasshass:
729 raise RuntimeError(f
"hass not set while rendering {self}")
731 if _render_info.get()
is not None:
733 f
"RenderInfo already set while rendering {self}, "
734 "this usually indicates the template is being rendered "
735 "in the wrong thread"
739 render_info._result = self.
templatetemplate.strip()
740 render_info._freeze_static()
743 token = _render_info.set(render_info)
746 variables, strict=strict, log_fn=log_fn, **kwargs
748 except TemplateError
as ex:
749 render_info.exception = ex
751 _render_info.reset(token)
753 render_info._freeze()
757 """Render template with value exposed.
759 If valid JSON will expose value_json too.
764 return run_callback_threadsafe(
775 error_value: Any = _SENTINEL,
776 variables: dict[str, Any] |
None =
None,
777 parse_result: bool =
False,
779 """Render template with value exposed.
781 If valid JSON will expose value_json too.
783 This method must be run in the event loop.
792 variables =
dict(variables
or {})
793 variables[
"value"] = value
797 except JSON_DECODE_EXCEPTIONS:
802 self.
templatetemplate, compiled, **variables
804 except jinja2.TemplateError
as ex:
805 if error_value
is _SENTINEL:
807 "Error parsing value: %s (value: %s, template: %s)",
812 return value
if error_value
is _SENTINEL
else error_value
814 if not parse_result
or self.
hasshass
and self.
hasshass.config.legacy_templates:
821 limited: bool =
False,
822 strict: bool =
False,
823 log_fn: Callable[[int, str],
None] |
None =
None,
824 ) -> jinja2.Template:
825 """Bind a template to a specific hass instance."""
828 assert self.
hasshass
is not None,
"hass variable not set on template"
831 ),
"can't change between limited and non limited template"
834 ),
"can't change between strict and non strict template"
835 assert not (strict
and limited),
"can't combine strict and limited template"
838 ),
"can't change custom log function"
839 assert self.
_compiled_code_compiled_code
is not None,
"template code was not compiled"
846 self.
_compiled_compiled = jinja2.Template.from_code(
853 """Compare template with another."""
856 and self.
templatetemplate == other.template
857 and self.
hasshass == other.hass
861 """Hash code for template."""
862 return self._hash_cache
865 """Representation of Template."""
866 return f
"Template<template=({self.template}) renders={self._renders}>"
870 def _domain_states(hass: HomeAssistant, name: str) -> DomainStates:
874 def _readonly(*args: Any, **kwargs: Any) -> Any:
875 """Raise an exception when a states object is modified."""
876 raise RuntimeError(f
"Cannot modify template States object: {args} {kwargs}")
880 """Class to expose all HA states as attributes."""
882 __setitem__ = _readonly
883 __delitem__ = _readonly
884 __slots__ = (
"_hass",)
887 """Initialize all states."""
888 self.
_hass_hass = hass
891 """Return the domain state."""
895 if name
in _RESERVED_NAMES:
905 __getitem__ = __getattr__
908 if (render_info := _render_info.get())
is not None:
909 render_info.all_states =
True
912 if (render_info := _render_info.get())
is not None:
913 render_info.all_states_lifecycle =
True
915 def __iter__(self) -> Generator[TemplateState]:
916 """Return all states."""
921 """Return number of states."""
923 return self.
_hass_hass.states.async_entity_ids_count()
928 rounded: bool | object = _SENTINEL,
929 with_unit: bool =
False,
931 """Return the states."""
935 if rounded
is _SENTINEL:
937 if rounded
or with_unit:
938 return state.format_state(rounded, with_unit)
942 """Representation of All States."""
943 return "<template AllStates>"
947 """Class to represent a translated state in a template."""
950 """Initialize all states."""
951 self.
_hass_hass = hass
953 def __call__(self, entity_id: str) -> str |
None:
954 """Retrieve translated state if available."""
960 state_value = state.state
961 domain = state.domain
962 device_class = state.attributes.get(
"device_class")
963 entry = entity_registry.async_get(self.
_hass_hass).
async_get(entity_id)
964 platform =
None if entry
is None else entry.platform
965 translation_key =
None if entry
is None else entry.translation_key
968 self.
_hass_hass, state_value, domain, platform, translation_key, device_class
972 """Representation of Translated state."""
973 return "<template StateTranslated>"
977 """Class to expose a specific HA domain as attributes."""
979 __slots__ = (
"_hass",
"_domain")
981 __setitem__ = _readonly
982 __delitem__ = _readonly
984 def __init__(self, hass: HomeAssistant, domain: str) ->
None:
985 """Initialize the domain states."""
986 self.
_hass_hass = hass
990 """Return the states."""
995 __getitem__ = __getattr__
998 if (entity_collect := _render_info.get())
is not None:
999 entity_collect.domains.add(self.
_domain_domain)
1002 if (entity_collect := _render_info.get())
is not None:
1003 entity_collect.domains_lifecycle.add(self.
_domain_domain)
1005 def __iter__(self) -> Generator[TemplateState]:
1006 """Return the iteration over all the states."""
1011 """Return number of states."""
1016 """Representation of Domain States."""
1017 return f
"<template DomainStates('{self._domain}')>"
1021 """Class to represent a state object in a template."""
1023 __slots__ = (
"_hass",
"_collect",
"_entity_id",
"_state")
1027 __setitem__ = _readonly
1028 __delitem__ = _readonly
1032 def __init__(self, hass: HomeAssistant, collect: bool, entity_id: str) ->
None:
1033 """Initialize template state."""
1034 self.
_hass_hass = hass
1037 self._cache: dict[str, Any] = {}
1040 if self.
_collect_collect
and (render_info := _render_info.get()):
1041 render_info.entities.add(self.
_entity_id_entity_id)
1046 """Return a property as an attribute for jinja."""
1047 if item
in _COLLECTABLE_STATE_ATTRIBUTES:
1049 if self.
_collect_collect
and (render_info := _render_info.get()):
1050 render_info.entities.add(self.
_entity_id_entity_id)
1051 return getattr(self._state, item)
1052 if item ==
"entity_id":
1054 if item ==
"state_with_unit":
1058 @under_cached_property
1060 """Wrap State.entity_id.
1062 Intentionally does not collect state
1067 def state(self) -> str:
1068 """Wrap State.state."""
1070 return self._state.state
1073 def attributes(self) -> ReadOnlyDict[str, Any]:
1074 """Wrap State.attributes."""
1076 return self._state.attributes
1080 """Wrap State.last_changed."""
1082 return self._state.last_changed
1086 """Wrap State.last_reported."""
1088 return self._state.last_reported
1092 """Wrap State.last_updated."""
1094 return self._state.last_updated
1098 """Wrap State.context."""
1100 return self._state.context
1104 """Wrap State.domain."""
1106 return self._state.domain
1110 """Wrap State.object_id."""
1112 return self._state.object_id
1115 def name(self) -> str:
1116 """Wrap State.name."""
1118 return self._state.name
1122 """Return the state concatenated with the unit if available."""
1123 return self.
format_stateformat_state(rounded=
True, with_unit=
True)
1125 def format_state(self, rounded: bool, with_unit: bool) -> str:
1126 """Return a formatted version of the state."""
1130 DOMAIN
as SENSOR_DOMAIN,
1131 async_rounded_state,
1135 if rounded
and self._state.domain == SENSOR_DOMAIN:
1138 state = self._state.state
1139 if with_unit
and (unit := self._state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)):
1140 return f
"{state} {unit}"
1143 def __eq__(self, other: object) -> bool:
1144 """Ensure we collect on equality check."""
1150 """Class to represent a state object in a template."""
1155 def __init__(self, hass: HomeAssistant, state: State, collect: bool =
True) ->
None:
1156 """Initialize template state."""
1157 super().
__init__(hass, collect, state.entity_id)
1161 """Representation of Template State."""
1162 return f
"<template TemplateState({self._state!r})>"
1166 """Class to represent a state object in a template."""
1171 self, hass: HomeAssistant, entity_id: str, collect: bool =
True
1173 """Initialize template state."""
1174 super().
__init__(hass, collect, entity_id)
1177 def _state(self) -> State:
1184 """Representation of Template State."""
1185 return f
"<template TemplateStateFromEntityId({self._entity_id})>"
1188 _create_template_state_no_collect = partial(TemplateState, collect=
False)
1192 if (entity_collect := _render_info.get())
is not None:
1193 entity_collect.entities.add(entity_id)
1197 hass: HomeAssistant, domain: str |
None
1198 ) -> Generator[TemplateState]:
1199 """State generator for a domain or all states."""
1200 states = hass.states
1209 container: Iterable[State]
1211 container = states._states.values()
1213 container = states.async_all(domain)
1214 for state
in container:
1219 state = hass.states.get(entity_id)
1225 def _get_state(hass: HomeAssistant, entity_id: str) -> TemplateState |
None:
1230 hass: HomeAssistant, entity_id: str, state: State |
None
1231 ) -> TemplateState |
None:
1241 hass: HomeAssistant, entity_id_or_state: Any
1242 ) -> State | TemplateState |
None:
1243 """Return state or entity_id if given."""
1244 if isinstance(entity_id_or_state, State):
1245 return entity_id_or_state
1246 if isinstance(entity_id_or_state, str):
1260 value: Any, default: _T | object = _SENTINEL
1261 ) -> bool | _T | object:
1262 """Try to convert value to a boolean."""
1265 from .
import config_validation
as cv
1267 return cv.boolean(value)
1269 if default
is _SENTINEL:
1275 """Convert the template result to a boolean.
1277 True/not 0/'1'/'true'/'yes'/'on'/'enable' are considered truthy
1278 False/0/None/'0'/'false'/'no'/'off'/'disable' are considered falsy
1279 All other values are falsy
1281 if template_result
is None:
1287 def expand(hass: HomeAssistant, *args: Any) -> Iterable[State]:
1288 """Expand out any groups and zones into entity states."""
1290 from .
import entity
as entity_helper
1294 sources = entity_helper.entity_sources(hass)
1296 entity = search.pop()
1297 if isinstance(entity, str):
1299 if (entity :=
_get_state(hass, entity))
is None:
1301 elif isinstance(entity, State):
1302 entity_id = entity.entity_id
1303 elif isinstance(entity, collections.abc.Iterable):
1310 if entity_id
in found:
1313 domain = entity.domain
1314 if domain ==
"group" or (
1315 (source := sources.get(entity_id))
and source[
"domain"] ==
"group"
1318 if group_entities := entity.attributes.get(ATTR_ENTITY_ID):
1319 search += group_entities
1320 elif domain ==
"zone":
1321 if zone_entities := entity.attributes.get(ATTR_PERSONS):
1322 search += zone_entities
1325 found[entity_id] = entity
1327 return list(found.values())
1330 def device_entities(hass: HomeAssistant, _device_id: str) -> Iterable[str]:
1331 """Get entity ids for entities tied to a device."""
1332 entity_reg = entity_registry.async_get(hass)
1333 entries = entity_registry.async_entries_for_device(entity_reg, _device_id)
1334 return [entry.entity_id
for entry
in entries]
1338 """Get entity ids for entities tied to an integration/domain.
1340 Provide entry_name as domain to get all entity id's for a integration/domain
1341 or provide a config entry title for filtering between instances of the same
1350 entities: list[str] = []
1351 ent_reg = entity_registry.async_get(hass)
1352 for entry
in hass.config_entries.async_entries():
1353 if entry.title != entry_name:
1355 entries = entity_registry.async_entries_for_config_entry(
1356 ent_reg, entry.entry_id
1358 entities.extend(entry.entity_id
for entry
in entries)
1364 from .entity
import entity_sources
1369 if info[
"domain"] == entry_name
1373 def config_entry_id(hass: HomeAssistant, entity_id: str) -> str |
None:
1374 """Get an config entry ID from an entity ID."""
1375 entity_reg = entity_registry.async_get(hass)
1376 if entity := entity_reg.async_get(entity_id):
1377 return entity.config_entry_id
1381 def device_id(hass: HomeAssistant, entity_id_or_device_name: str) -> str |
None:
1382 """Get a device ID from an entity ID or device name."""
1383 entity_reg = entity_registry.async_get(hass)
1384 entity = entity_reg.async_get(entity_id_or_device_name)
1385 if entity
is not None:
1386 return entity.device_id
1388 dev_reg = device_registry.async_get(hass)
1392 for device_id, device
in dev_reg.devices.items()
1393 if (name := device.name_by_user
or device.name)
1394 and (
str(entity_id_or_device_name) == name)
1400 def device_attr(hass: HomeAssistant, device_or_entity_id: str, attr_name: str) -> Any:
1401 """Get the device specific attribute."""
1402 device_reg = device_registry.async_get(hass)
1403 if not isinstance(device_or_entity_id, str):
1407 "." in device_or_entity_id
1408 and (_device_id :=
device_id(hass, device_or_entity_id))
is not None
1410 device = device_reg.async_get(_device_id)
1411 elif "." not in device_or_entity_id:
1412 device = device_reg.async_get(device_or_entity_id)
1413 if device
is None or not hasattr(device, attr_name):
1415 return getattr(device, attr_name)
1419 hass: HomeAssistant, config_entry_id_: str, attr_name: str
1421 """Get config entry specific attribute."""
1422 if not isinstance(config_entry_id_, str):
1425 if attr_name
not in (
"domain",
"title",
"state",
"source",
"disabled_by"):
1428 config_entry = hass.config_entries.async_get_entry(config_entry_id_)
1430 if config_entry
is None:
1433 return getattr(config_entry, attr_name)
1437 hass: HomeAssistant, device_or_entity_id: str, attr_name: str, attr_value: Any
1439 """Test if a device's attribute is a specific value."""
1440 return bool(
device_attr(hass, device_or_entity_id, attr_name) == attr_value)
1443 def issues(hass: HomeAssistant) -> dict[tuple[str, str], dict[str, Any]]:
1444 """Return all open issues."""
1445 current_issues = issue_registry.async_get(hass).issues
1447 return {k: v.to_json()
for (k, v)
in current_issues.items()}
1450 def issue(hass: HomeAssistant, domain: str, issue_id: str) -> dict[str, Any] |
None:
1451 """Get issue by domain and issue_id."""
1452 result = issue_registry.async_get(hass).async_get_issue(domain, issue_id)
1454 return result.to_json()
1458 def floors(hass: HomeAssistant) -> Iterable[str |
None]:
1459 """Return all floors."""
1460 floor_registry = fr.async_get(hass)
1461 return [floor.floor_id
for floor
in floor_registry.async_list_floors()]
1464 def floor_id(hass: HomeAssistant, lookup_value: Any) -> str |
None:
1465 """Get the floor ID from a floor name."""
1466 floor_registry = fr.async_get(hass)
1467 if floor := floor_registry.async_get_floor_by_name(
str(lookup_value)):
1468 return floor.floor_id
1470 if aid :=
area_id(hass, lookup_value):
1471 area_reg = area_registry.async_get(hass)
1472 if area := area_reg.async_get_area(aid):
1473 return area.floor_id
1478 def floor_name(hass: HomeAssistant, lookup_value: str) -> str |
None:
1479 """Get the floor name from a floor id."""
1480 floor_registry = fr.async_get(hass)
1481 if floor := floor_registry.async_get_floor(lookup_value):
1484 if aid :=
area_id(hass, lookup_value):
1485 area_reg = area_registry.async_get(hass)
1487 (area := area_reg.async_get_area(aid))
1489 and (floor := floor_registry.async_get_floor(area.floor_id))
1496 def floor_areas(hass: HomeAssistant, floor_id_or_name: str) -> Iterable[str]:
1497 """Return area IDs for a given floor ID or name."""
1498 _floor_id: str |
None
1501 if floor_name(hass, floor_id_or_name)
is not None:
1502 _floor_id = floor_id_or_name
1504 _floor_id =
floor_id(hass, floor_id_or_name)
1505 if _floor_id
is None:
1508 area_reg = area_registry.async_get(hass)
1509 entries = area_registry.async_entries_for_floor(area_reg, _floor_id)
1510 return [entry.id
for entry
in entries
if entry.id]
1513 def areas(hass: HomeAssistant) -> Iterable[str |
None]:
1514 """Return all areas."""
1515 return list(area_registry.async_get(hass).areas)
1518 def area_id(hass: HomeAssistant, lookup_value: str) -> str |
None:
1519 """Get the area ID from an area name, device id, or entity id."""
1520 area_reg = area_registry.async_get(hass)
1521 if area := area_reg.async_get_area_by_name(
str(lookup_value)):
1524 ent_reg = entity_registry.async_get(hass)
1525 dev_reg = device_registry.async_get(hass)
1527 from .
import config_validation
as cv
1530 cv.entity_id(lookup_value)
1534 if entity := ent_reg.async_get(lookup_value):
1537 return entity.area_id
1539 if entity.device_id
and (device := dev_reg.async_get(entity.device_id)):
1540 return device.area_id
1543 if device := dev_reg.async_get(lookup_value):
1544 return device.area_id
1549 def _get_area_name(area_reg: area_registry.AreaRegistry, valid_area_id: str) -> str:
1550 """Get area name from valid area ID."""
1551 area = area_reg.async_get_area(valid_area_id)
1556 def area_name(hass: HomeAssistant, lookup_value: str) -> str |
None:
1557 """Get the area name from an area id, device id, or entity id."""
1558 area_reg = area_registry.async_get(hass)
1559 if area := area_reg.async_get_area(lookup_value):
1562 dev_reg = device_registry.async_get(hass)
1563 ent_reg = entity_registry.async_get(hass)
1565 from .
import config_validation
as cv
1568 cv.entity_id(lookup_value)
1572 if entity := ent_reg.async_get(lookup_value):
1580 and (device := dev_reg.async_get(entity.device_id))
1585 if (device := dev_reg.async_get(lookup_value))
and device.area_id:
1591 def area_entities(hass: HomeAssistant, area_id_or_name: str) -> Iterable[str]:
1592 """Return entities for a given area ID or name."""
1593 _area_id: str |
None
1596 if area_name(hass, area_id_or_name)
is None:
1597 _area_id =
area_id(hass, area_id_or_name)
1599 _area_id = area_id_or_name
1600 if _area_id
is None:
1602 ent_reg = entity_registry.async_get(hass)
1605 for entry
in entity_registry.async_entries_for_area(ent_reg, _area_id)
1607 dev_reg = device_registry.async_get(hass)
1613 for device
in device_registry.async_entries_for_area(dev_reg, _area_id)
1614 for entity
in entity_registry.async_entries_for_device(ent_reg, device.id)
1615 if entity.area_id
is None
1621 def area_devices(hass: HomeAssistant, area_id_or_name: str) -> Iterable[str]:
1622 """Return device IDs for a given area ID or name."""
1623 _area_id: str |
None
1626 if area_name(hass, area_id_or_name)
is not None:
1627 _area_id = area_id_or_name
1629 _area_id =
area_id(hass, area_id_or_name)
1630 if _area_id
is None:
1632 dev_reg = device_registry.async_get(hass)
1633 entries = device_registry.async_entries_for_area(dev_reg, _area_id)
1634 return [entry.id
for entry
in entries]
1637 def labels(hass: HomeAssistant, lookup_value: Any =
None) -> Iterable[str |
None]:
1638 """Return all labels, or those from a area ID, device ID, or entity ID."""
1639 label_reg = label_registry.async_get(hass)
1640 if lookup_value
is None:
1641 return list(label_reg.labels)
1643 ent_reg = entity_registry.async_get(hass)
1646 from .
import config_validation
as cv
1648 lookup_value =
str(lookup_value)
1651 cv.entity_id(lookup_value)
1655 if entity := ent_reg.async_get(lookup_value):
1656 return list(entity.labels)
1659 dev_reg = device_registry.async_get(hass)
1660 if device := dev_reg.async_get(lookup_value):
1661 return list(device.labels)
1664 area_reg = area_registry.async_get(hass)
1665 if area := area_reg.async_get_area(lookup_value):
1666 return list(area.labels)
1671 def label_id(hass: HomeAssistant, lookup_value: Any) -> str |
None:
1672 """Get the label ID from a label name."""
1673 label_reg = label_registry.async_get(hass)
1674 if label := label_reg.async_get_label_by_name(
str(lookup_value)):
1675 return label.label_id
1679 def label_name(hass: HomeAssistant, lookup_value: str) -> str |
None:
1680 """Get the label name from a label ID."""
1681 label_reg = label_registry.async_get(hass)
1682 if label := label_reg.async_get_label(lookup_value):
1687 def _label_id_or_name(hass: HomeAssistant, label_id_or_name: str) -> str |
None:
1688 """Get the label ID from a label name or ID."""
1691 if label_name(hass, label_id_or_name)
is not None:
1692 return label_id_or_name
1693 return label_id(hass, label_id_or_name)
1696 def label_areas(hass: HomeAssistant, label_id_or_name: str) -> Iterable[str]:
1697 """Return areas for a given label ID or name."""
1700 area_reg = area_registry.async_get(hass)
1701 entries = area_registry.async_entries_for_label(area_reg, _label_id)
1702 return [entry.id
for entry
in entries]
1705 def label_devices(hass: HomeAssistant, label_id_or_name: str) -> Iterable[str]:
1706 """Return device IDs for a given label ID or name."""
1709 dev_reg = device_registry.async_get(hass)
1710 entries = device_registry.async_entries_for_label(dev_reg, _label_id)
1711 return [entry.id
for entry
in entries]
1714 def label_entities(hass: HomeAssistant, label_id_or_name: str) -> Iterable[str]:
1715 """Return entities for a given label ID or name."""
1718 ent_reg = entity_registry.async_get(hass)
1719 entries = entity_registry.async_entries_for_label(ent_reg, _label_id)
1720 return [entry.entity_id
for entry
in entries]
1724 """Find closest entity.
1728 closest(states.device_tracker)
1729 closest('group.children')
1730 closest(states.group.children)
1733 closest(23.456, 23.456, 'group.children')
1734 closest('zone.school', 'group.children')
1735 closest(states.zone.school, 'group.children')
1739 states.device_tracker | closest
1740 ['group.children', states.device_tracker] | closest
1741 'group.children' | closest(23.456, 23.456)
1742 states.device_tracker | closest('zone.school')
1743 'group.children' | closest(states.zone.school)
1747 latitude = hass.config.latitude
1748 longitude = hass.config.longitude
1751 elif len(args) == 2:
1754 if point_state
is None:
1755 _LOGGER.warning(
"Closest:Unable to find state %s", args[0])
1757 if not loc_helper.has_location(point_state):
1759 "Closest:State does not contain valid location: %s", point_state
1763 latitude = point_state.attributes.get(ATTR_LATITUDE)
1764 longitude = point_state.attributes.get(ATTR_LONGITUDE)
1769 latitude = convert(args[0], float)
1770 longitude = convert(args[1], float)
1772 if latitude
is None or longitude
is None:
1774 "Closest:Received invalid coordinates: %s, %s", args[0], args[1]
1780 states =
expand(hass, entities)
1783 return loc_helper.closest(latitude, longitude, states)
1787 """Call closest as a filter. Need to reorder arguments."""
1788 new_args =
list(args[1:])
1789 new_args.append(args[0])
1790 return closest(hass, *new_args)
1794 """Calculate distance.
1796 Will calculate distance from home to a point or between points.
1797 Points can be passed in using state objects or lat/lng coordinates.
1801 to_process =
list(args)
1804 value = to_process.pop(0)
1810 if point_state
is None:
1814 "Distance:Expected latitude and longitude, got %s", value
1818 value_2 = to_process.pop(0)
1819 latitude = convert(value, float)
1820 longitude = convert(value_2, float)
1822 if latitude
is None or longitude
is None:
1824 "Distance:Unable to process latitude and longitude: %s, %s",
1831 if not loc_helper.has_location(point_state):
1833 "Distance:State does not contain valid location: %s", point_state
1837 latitude = point_state.attributes.get(ATTR_LATITUDE)
1838 longitude = point_state.attributes.get(ATTR_LONGITUDE)
1840 locations.append((latitude, longitude))
1842 if len(locations) == 1:
1843 return hass.config.distance(*locations[0])
1845 return hass.config.units.length(
1846 loc_util.distance(*locations[0] + locations[1]), UnitOfLength.METERS
1851 """Test if an entity is hidden."""
1852 entity_reg = entity_registry.async_get(hass)
1853 entry = entity_reg.async_get(entity_id)
1854 return entry
is not None and entry.hidden
1857 def is_state(hass: HomeAssistant, entity_id: str, state: str | list[str]) -> bool:
1858 """Test if a state is a specific value."""
1860 return state_obj
is not None and (
1861 state_obj.state == state
or isinstance(state, list)
and state_obj.state
in state
1865 def is_state_attr(hass: HomeAssistant, entity_id: str, name: str, value: Any) -> bool:
1866 """Test if a state's attribute is a specific value."""
1868 return attr
is not None and attr == value
1871 def state_attr(hass: HomeAssistant, entity_id: str, name: str) -> Any:
1872 """Get a specific attribute from a state."""
1873 if (state_obj :=
_get_state(hass, entity_id))
is not None:
1874 return state_obj.attributes.get(name)
1878 def has_value(hass: HomeAssistant, entity_id: str) -> bool:
1879 """Test if an entity has a valid value."""
1882 return state_obj
is not None and (
1883 state_obj.state
not in [STATE_UNAVAILABLE, STATE_UNKNOWN]
1887 def now(hass: HomeAssistant) -> datetime:
1888 """Record fetching now."""
1889 if (render_info := _render_info.get())
is not None:
1890 render_info.has_time =
True
1892 return dt_util.now()
1895 def utcnow(hass: HomeAssistant) -> datetime:
1896 """Record fetching utcnow."""
1897 if (render_info := _render_info.get())
is not None:
1898 render_info.has_time =
True
1900 return dt_util.utcnow()
1904 """Log warning if no default is specified."""
1905 template, action = template_cv.get()
or (
"",
"rendering or compiling")
1907 f
"Template error: {function} got invalid input '{value}' when {action} template"
1908 f
" '{template}' but no default was specified"
1912 def forgiving_round(value, precision=0, method="common", default=_SENTINEL):
1913 """Filter to round a value."""
1916 multiplier =
float(10**precision)
1917 if method ==
"ceil":
1918 value = math.ceil(
float(value) * multiplier) / multiplier
1919 elif method ==
"floor":
1920 value = math.floor(
float(value) * multiplier) / multiplier
1921 elif method ==
"half":
1922 value = round(
float(value) * 2) / 2
1925 value = round(
float(value), precision)
1926 return int(value)
if precision == 0
else value
1927 except (ValueError, TypeError):
1929 if default
is _SENTINEL:
1934 def multiply(value, amount, default=_SENTINEL):
1935 """Filter to convert value to float and multiply it."""
1938 except (ValueError, TypeError):
1940 if default
is _SENTINEL:
1945 def add(value, amount, default=_SENTINEL):
1946 """Filter to convert value to float and add it."""
1949 except (ValueError, TypeError):
1951 if default
is _SENTINEL:
1956 def logarithm(value, base=math.e, default=_SENTINEL):
1957 """Filter and function to get logarithm of the value with a specific base."""
1960 except (ValueError, TypeError):
1961 if default
is _SENTINEL:
1965 value_float =
float(value)
1966 return math.log(value_float, base_float)
1967 except (ValueError, TypeError):
1968 if default
is _SENTINEL:
1973 def sine(value, default=_SENTINEL):
1974 """Filter and function to get sine of the value."""
1977 except (ValueError, TypeError):
1978 if default
is _SENTINEL:
1983 def cosine(value, default=_SENTINEL):
1984 """Filter and function to get cosine of the value."""
1987 except (ValueError, TypeError):
1988 if default
is _SENTINEL:
1993 def tangent(value, default=_SENTINEL):
1994 """Filter and function to get tangent of the value."""
1997 except (ValueError, TypeError):
1998 if default
is _SENTINEL:
2003 def arc_sine(value, default=_SENTINEL):
2004 """Filter and function to get arc sine of the value."""
2007 except (ValueError, TypeError):
2008 if default
is _SENTINEL:
2014 """Filter and function to get arc cosine of the value."""
2017 except (ValueError, TypeError):
2018 if default
is _SENTINEL:
2024 """Filter and function to get arc tangent of the value."""
2027 except (ValueError, TypeError):
2028 if default
is _SENTINEL:
2034 """Filter and function to calculate four quadrant arc tangent of y / x.
2036 The parameters to atan2 may be passed either in an iterable or as separate arguments
2037 The default value may be passed either as a positional or in a keyword argument
2040 if 1 <= len(args) <= 2
and isinstance(args[0], (list, tuple)):
2041 if len(args) == 2
and default
is _SENTINEL:
2045 elif len(args) == 3
and default
is _SENTINEL:
2049 return math.atan2(
float(args[0]),
float(args[1]))
2050 except (ValueError, TypeError):
2051 if default
is _SENTINEL:
2057 """Filter and function to get version object of the value."""
2058 return AwesomeVersion(value)
2062 """Filter and function to get square root of the value."""
2065 except (ValueError, TypeError):
2066 if default
is _SENTINEL:
2071 def timestamp_custom(value, date_format=DATE_STR_FORMAT, local=True, default=_SENTINEL):
2072 """Filter to convert given timestamp to format."""
2074 result = dt_util.utc_from_timestamp(value)
2077 result = dt_util.as_local(result)
2079 return result.strftime(date_format)
2080 except (ValueError, TypeError):
2082 if default
is _SENTINEL:
2088 """Filter to convert given timestamp to local date/time."""
2090 return dt_util.as_local(dt_util.utc_from_timestamp(value)).isoformat()
2091 except (ValueError, TypeError):
2093 if default
is _SENTINEL:
2099 """Filter to convert given timestamp to UTC date/time."""
2101 return dt_util.utc_from_timestamp(value).isoformat()
2102 except (ValueError, TypeError):
2104 if default
is _SENTINEL:
2110 """Filter and function which tries to convert value to timestamp."""
2112 return dt_util.as_timestamp(value)
2113 except (ValueError, TypeError):
2114 if default
is _SENTINEL:
2119 def as_datetime(value: Any, default: Any = _SENTINEL) -> Any:
2120 """Filter and to convert a time string or UNIX timestamp to datetime object."""
2122 if type(value)
is datetime:
2125 if type(value)
is date:
2126 return datetime.combine(value,
time(0, 0, 0))
2129 timestamp =
float(value)
2130 return dt_util.utc_from_timestamp(timestamp)
2131 except (ValueError, TypeError):
2134 return dt_util.parse_datetime(value, raise_on_error=
True)
2135 except (ValueError, TypeError):
2136 if default
is _SENTINEL:
2139 if isinstance(value, str):
2146 """Parse a ISO8601 duration like 'PT10M' to a timedelta."""
2147 return dt_util.parse_duration(value)
2151 """Merge action responses into single list.
2153 Checks that the input is a correct service response:
2155 "entity_id": {str: dict[str, Any]},
2157 If response is a single list, it will extend the list with the items
2158 and add the entity_id and value_key to each dictionary for reference.
2159 If response is a dictionary or multiple lists,
2160 it will append the dictionary/lists to the list
2161 and add the entity_id to each dictionary for reference.
2163 if not isinstance(value, dict):
2164 raise TypeError(
"Response is not a dictionary")
2169 is_single_list =
False
2170 response_items: list = []
2171 input_service_response = deepcopy(value)
2172 for entity_id, entity_response
in input_service_response.items():
2173 if not isinstance(entity_response, dict):
2174 raise TypeError(
"Response is not a dictionary")
2175 for value_key, type_response
in entity_response.items():
2176 if len(entity_response) == 1
and isinstance(type_response, list):
2180 is_single_list =
True
2181 for dict_in_list
in type_response:
2182 if isinstance(dict_in_list, dict):
2183 if ATTR_ENTITY_ID
in dict_in_list:
2185 f
"Response dictionary already contains key '{ATTR_ENTITY_ID}'"
2187 dict_in_list[ATTR_ENTITY_ID] = entity_id
2188 dict_in_list[
"value_key"] = value_key
2189 response_items.extend(type_response)
2195 if not is_single_list:
2196 _response = entity_response.copy()
2197 if ATTR_ENTITY_ID
in _response:
2199 f
"Response dictionary already contains key '{ATTR_ENTITY_ID}'"
2201 _response[ATTR_ENTITY_ID] = entity_id
2202 response_items.append(_response)
2204 return response_items
2207 def strptime(string, fmt, default=_SENTINEL):
2208 """Parse a time string to datetime."""
2210 return datetime.strptime(string, fmt)
2211 except (ValueError, AttributeError, TypeError):
2212 if default
is _SENTINEL:
2218 """Filter to force a failure when the value is undefined."""
2219 if isinstance(value, jinja2.Undefined):
2225 """Convert a built-in min/max Jinja filter to a global function.
2227 The parameters may be passed as an iterable or as separate arguments.
2231 @wraps(builtin_filter)
2232 def wrapper(environment: jinja2.Environment, *args: Any, **kwargs: Any) -> Any:
2234 raise TypeError(f
"{name} expected at least 1 argument, got 0")
2237 if isinstance(args[0], Iterable):
2238 return builtin_filter(environment, args[0], **kwargs)
2240 raise TypeError(f
"'{type(args[0]).__name__}' object is not iterable")
2242 return builtin_filter(environment, args, **kwargs)
2244 return pass_environment(wrapper)
2247 def average(*args: Any, default: Any = _SENTINEL) -> Any:
2248 """Filter and function to calculate the arithmetic mean.
2250 Calculates of an iterable or of two or more arguments.
2252 The parameters may be passed as an iterable or as separate arguments.
2255 raise TypeError(
"average expected at least 1 argument, got 0")
2259 if isinstance(args[0], Iterable):
2260 average_list = args[0]
2261 if len(args) > 1
and default
is _SENTINEL:
2263 elif len(args) == 1:
2264 raise TypeError(f
"'{type(args[0]).__name__}' object is not iterable")
2269 return statistics.fmean(average_list)
2270 except (TypeError, statistics.StatisticsError):
2271 if default
is _SENTINEL:
2276 def median(*args: Any, default: Any = _SENTINEL) -> Any:
2277 """Filter and function to calculate the median.
2279 Calculates median of an iterable of two or more arguments.
2281 The parameters may be passed as an iterable or as separate arguments.
2284 raise TypeError(
"median expected at least 1 argument, got 0")
2288 if isinstance(args[0], Iterable):
2289 median_list = args[0]
2290 if len(args) > 1
and default
is _SENTINEL:
2292 elif len(args) == 1:
2293 raise TypeError(f
"'{type(args[0]).__name__}' object is not iterable")
2298 return statistics.median(median_list)
2299 except (TypeError, statistics.StatisticsError):
2300 if default
is _SENTINEL:
2306 """Filter and function to calculate the statistical mode.
2308 Calculates mode of an iterable of two or more arguments.
2310 The parameters may be passed as an iterable or as separate arguments.
2313 raise TypeError(
"statistical_mode expected at least 1 argument, got 0")
2317 if len(args) == 1
and isinstance(args[0], Iterable):
2319 elif isinstance(args[0], list | tuple):
2321 if len(args) > 1
and default
is _SENTINEL:
2323 elif len(args) == 1:
2324 raise TypeError(f
"'{type(args[0]).__name__}' object is not iterable")
2329 return statistics.mode(mode_list)
2330 except (TypeError, statistics.StatisticsError):
2331 if default
is _SENTINEL:
2337 """Try to convert value to a float."""
2340 except (ValueError, TypeError):
2341 if default
is _SENTINEL:
2347 """Try to convert value to a float."""
2350 except (ValueError, TypeError):
2351 if default
is _SENTINEL:
2357 """Try to convert value to an int, and raise if it fails."""
2358 result = jinja2.filters.do_int(value, default=default, base=base)
2359 if result
is _SENTINEL:
2365 """Try to convert value to an int, and raise if it fails."""
2366 result = jinja2.filters.do_int(value, default=default, base=base)
2367 if result
is _SENTINEL:
2373 """Try to convert value to a float."""
2376 except (ValueError, TypeError):
2378 if not math.isfinite(fvalue):
2384 """Return whether a value is a list."""
2385 return isinstance(value, list)
2388 def _is_set(value: Any) -> bool:
2389 """Return whether a value is a set."""
2390 return isinstance(value, set)
2394 """Return whether a value is a tuple."""
2395 return isinstance(value, tuple)
2398 def _to_set(value: Any) -> set[Any]:
2399 """Convert value to set."""
2404 """Convert value to tuple."""
2409 """Return whether a value is a datetime."""
2410 return isinstance(value, datetime)
2414 """Return whether a value is a string or string like object."""
2415 return isinstance(value, (str, bytes, bytearray))
2418 def regex_match(value, find="", ignorecase=False):
2419 """Match value using regex."""
2420 if not isinstance(value, str):
2422 flags = re.IGNORECASE
if ignorecase
else 0
2426 _regex_cache = lru_cache(maxsize=128)(re.compile)
2430 """Replace using regex."""
2431 if not isinstance(value, str):
2433 flags = re.IGNORECASE
if ignorecase
else 0
2438 """Search using regex."""
2439 if not isinstance(value, str):
2441 flags = re.IGNORECASE
if ignorecase
else 0
2446 """Find all matches using regex and then pick specific match index."""
2451 """Find all matches using regex."""
2452 if not isinstance(value, str):
2454 flags = re.IGNORECASE
if ignorecase
else 0
2459 """Perform a bitwise and operation."""
2460 return first_value & second_value
2464 """Perform a bitwise or operation."""
2465 return first_value | second_value
2469 """Perform a bitwise xor operation."""
2470 return first_value ^ second_value
2473 def struct_pack(value: Any |
None, format_string: str) -> bytes |
None:
2474 """Pack an object into a bytes object."""
2476 return pack(format_string, value)
2480 "Template warning: 'pack' unable to pack object '%s' with type '%s' and"
2481 " format_string '%s' see https://docs.python.org/3/library/struct.html"
2482 " for more information"
2485 type(value).__name__,
2491 def struct_unpack(value: bytes, format_string: str, offset: int = 0) -> Any |
None:
2492 """Unpack an object from bytes an return the first native object."""
2494 return unpack_from(format_string, value, offset)[0]
2498 "Template warning: 'unpack' unable to unpack object '%s' with"
2499 " format_string '%s' and offset %s see"
2500 " https://docs.python.org/3/library/struct.html for more information"
2510 """Perform base64 encode."""
2511 return base64.b64encode(value.encode(
"utf-8")).decode(
"utf-8")
2514 def base64_decode(value: str, encoding: str |
None =
"utf-8") -> str | bytes:
2515 """Perform base64 decode."""
2516 decoded = base64.b64decode(value)
2518 return decoded.decode(encoding)
2524 """Perform ordinal conversion."""
2525 suffixes = [
"th",
"st",
"nd",
"rd"] + [
"th"] * 6
2527 suffixes[(
int(
str(value)[-1])) % 10]
2528 if int(
str(value)[-2:]) % 100
not in range(11, 14)
2534 """Convert a JSON string to an object."""
2539 """Disable custom types in json serialization."""
2540 raise TypeError(f
"Object of type {type(obj).__name__} is not JSON serializable")
2545 ensure_ascii: bool =
False,
2546 pretty_print: bool =
False,
2547 sort_keys: bool =
False,
2549 """Convert an object to a JSON string."""
2554 ensure_ascii=ensure_ascii,
2555 indent=2
if pretty_print
else None,
2556 sort_keys=sort_keys,
2560 ORJSON_PASSTHROUGH_OPTIONS
2564 | orjson.OPT_NON_STR_KEYS
2565 | (orjson.OPT_INDENT_2
if pretty_print
else 0)
2566 | (orjson.OPT_SORT_KEYS
if sort_keys
else 0)
2569 return orjson.dumps(
2572 default=_to_json_default,
2578 """Choose a random value.
2580 Unlike Jinja's random filter,
2581 this is context-dependent to avoid caching the chosen value.
2583 return random.choice(values)
2586 def today_at(hass: HomeAssistant, time_str: str =
"") -> datetime:
2587 """Record fetching now where the time has been replaced with value."""
2588 if (render_info := _render_info.get())
is not None:
2589 render_info.has_time =
True
2591 today = dt_util.start_of_local_day()
2595 if (time_today := dt_util.parse_time(time_str))
is None:
2597 f
"could not convert {type(time_str).__name__} to datetime: '{time_str}'"
2600 return datetime.combine(today, time_today, today.tzinfo)
2604 """Take a datetime and return its "age" as a string.
2606 The age can be in second, minute, hour, day, month or year. Only the
2607 biggest unit is considered, e.g. if it's 2 days and 3 hours, "2 days" will
2609 If the input datetime is in the future,
2610 the input datetime will be returned.
2612 If the input are not a datetime object the input will be returned unmodified.
2614 Note: This template function is deprecated in favor of `time_until`, but is still
2615 supported so as not to break old templates.
2618 if (render_info := _render_info.get())
is not None:
2619 render_info.has_time =
True
2621 if not isinstance(value, datetime):
2623 if not value.tzinfo:
2624 value = dt_util.as_local(value)
2625 if dt_util.now() < value:
2627 return dt_util.get_age(value)
2630 def time_since(hass: HomeAssistant, value: Any | datetime, precision: int = 1) -> Any:
2631 """Take a datetime and return its "age" as a string.
2633 The age can be in seconds, minutes, hours, days, months and year.
2635 precision is the number of units to return, with the last unit rounded.
2637 If the value not a datetime object the input will be returned unmodified.
2639 if (render_info := _render_info.get())
is not None:
2640 render_info.has_time =
True
2642 if not isinstance(value, datetime):
2644 if not value.tzinfo:
2645 value = dt_util.as_local(value)
2646 if dt_util.now() < value:
2649 return dt_util.get_age(value, precision)
2652 def time_until(hass: HomeAssistant, value: Any | datetime, precision: int = 1) -> Any:
2653 """Take a datetime and return the amount of time until that time as a string.
2655 The time until can be in seconds, minutes, hours, days, months and years.
2657 precision is the number of units to return, with the last unit rounded.
2659 If the value not a datetime object the input will be returned unmodified.
2661 if (render_info := _render_info.get())
is not None:
2662 render_info.has_time =
True
2664 if not isinstance(value, datetime):
2666 if not value.tzinfo:
2667 value = dt_util.as_local(value)
2668 if dt_util.now() > value:
2671 return dt_util.get_time_remaining(value, precision)
2675 """Urlencode dictionary and return as UTF-8 string."""
2676 return urllib_urlencode(value).encode(
"utf-8")
2679 def slugify(value, separator="_"):
2680 """Convert a string into a slug, such as what is used for entity ids."""
2681 return slugify_util(value, separator=separator)
2685 value: Any, if_true: Any =
True, if_false: Any =
False, if_none: Any = _SENTINEL
2687 """Immediate if function/filter that allow for common if/else constructs.
2689 https://en.wikipedia.org/wiki/IIf
2692 {{ is_state("device_tracker.frenck", "home") | iif("yes", "no") }}
2693 {{ iif(1==2, "yes", "no") }}
2694 {{ (1 == 1) | iif("yes", "no") }}
2697 if value
is None and if_none
is not _SENTINEL:
2705 """Context manager to store template being parsed or rendered in a ContextVar."""
2708 """Store template being parsed or rendered in a Contextvar to aid error handling."""
2709 template_cv.set((template_str, action))
2713 exc_type: type[BaseException] |
None,
2714 exc_value: BaseException |
None,
2715 traceback: TracebackType |
None,
2717 """Raise any exception triggered within the runtime context."""
2718 template_cv.set(
None)
2725 template_str: str, template: jinja2.Template, **kwargs: Any
2727 """Store template being rendered in a ContextVar to aid error handling."""
2728 with _template_context_manager
as cm:
2729 cm.set_template(template_str,
"rendering")
2730 return template.render(**kwargs)
2734 strict: bool |
None, log_fn: Callable[[int, str],
None] |
None
2735 ) -> type[jinja2.Undefined]:
2736 """Log on undefined variables."""
2739 return jinja2.StrictUndefined
2741 def _log_with_logger(level: int, msg: str) ->
None:
2742 template, action = template_cv.get()
or (
"",
"rendering or compiling")
2745 "Template variable %s: %s when %s '%s'",
2746 logging.getLevelName(level).lower(),
2752 _log_fn = log_fn
or _log_with_logger
2754 class LoggingUndefined(jinja2.Undefined):
2755 """Log on undefined variables."""
2757 def _log_message(self) -> None:
2758 _log_fn(logging.WARNING, self._undefined_message)
2760 def _fail_with_undefined_error(self, *args, **kwargs):
2762 return super()._fail_with_undefined_error(*args, **kwargs)
2763 except self._undefined_exception:
2764 _log_fn(logging.ERROR, self._undefined_message)
2767 def __str__(self) -> str:
2768 """Log undefined __str___."""
2770 return super().__str__()
2773 """Log undefined __iter___."""
2775 return super().__iter__()
2777 def __bool__(self) -> bool:
2778 """Log undefined __bool___."""
2780 return super().__bool__()
2782 return LoggingUndefined
2786 """Load all custom jinja files under 5MiB into memory."""
2787 custom_templates = await hass.async_add_executor_job(_load_custom_templates, hass)
2793 jinja_path = hass.config.path(
"custom_templates")
2796 for item
in pathlib.Path(jinja_path).rglob(
"*.jinja")
2797 if item.is_file()
and item.stat().st_size <= MAX_CUSTOM_TEMPLATE_SIZE
2799 for file
in all_files:
2800 content = file.read_text()
2801 path =
str(file.relative_to(jinja_path))
2802 result[path] = content
2806 @singleton(_HASS_LOADER)
2812 """An in-memory jinja loader that keeps track of templates that need to be reloaded."""
2815 """Initialize an empty HassLoader."""
2821 """Map filename to jinja source."""
2825 def sources(self, value: dict[str, str]) ->
None:
2830 self, environment: jinja2.Environment, template: str
2831 ) -> tuple[str, str |
None, Callable[[], bool] |
None]:
2832 """Get in-memory sources."""
2833 if template
not in self.
_sources_sources:
2834 raise jinja2.TemplateNotFound(template)
2835 cur_reload = self.
_reload_reload
2836 return self.
_sources_sources[template], template,
lambda: cur_reload == self.
_reload_reload
2840 """The Home Assistant template environment."""
2844 hass: HomeAssistant |
None,
2845 limited: bool |
None =
False,
2846 strict: bool |
None =
False,
2847 log_fn: Callable[[int, str],
None] |
None =
None,
2849 """Initialise template environment."""
2851 self.
hasshass = hass
2852 self.template_cache: weakref.WeakValueDictionary[
2853 str | jinja2.nodes.Template, CodeType |
None
2854 ] = weakref.WeakValueDictionary()
2855 self.add_extension(
"jinja2.ext.loopcontrols")
2856 self.filters[
"round"] = forgiving_round
2857 self.filters[
"multiply"] = multiply
2858 self.filters[
"add"] = add
2859 self.filters[
"log"] = logarithm
2860 self.filters[
"sin"] = sine
2861 self.filters[
"cos"] = cosine
2862 self.filters[
"tan"] = tangent
2863 self.filters[
"asin"] = arc_sine
2864 self.filters[
"acos"] = arc_cosine
2865 self.filters[
"atan"] = arc_tangent
2866 self.filters[
"atan2"] = arc_tangent2
2867 self.filters[
"sqrt"] = square_root
2868 self.filters[
"as_datetime"] = as_datetime
2869 self.filters[
"as_timedelta"] = as_timedelta
2870 self.filters[
"as_timestamp"] = forgiving_as_timestamp
2871 self.filters[
"as_local"] = dt_util.as_local
2872 self.filters[
"timestamp_custom"] = timestamp_custom
2873 self.filters[
"timestamp_local"] = timestamp_local
2874 self.filters[
"timestamp_utc"] = timestamp_utc
2875 self.filters[
"to_json"] = to_json
2876 self.filters[
"from_json"] = from_json
2877 self.filters[
"is_defined"] = fail_when_undefined
2878 self.filters[
"average"] = average
2879 self.filters[
"median"] = median
2880 self.filters[
"statistical_mode"] = statistical_mode
2881 self.filters[
"random"] = random_every_time
2882 self.filters[
"base64_encode"] = base64_encode
2883 self.filters[
"base64_decode"] = base64_decode
2884 self.filters[
"ordinal"] = ordinal
2885 self.filters[
"regex_match"] = regex_match
2886 self.filters[
"regex_replace"] = regex_replace
2887 self.filters[
"regex_search"] = regex_search
2888 self.filters[
"regex_findall"] = regex_findall
2889 self.filters[
"regex_findall_index"] = regex_findall_index
2890 self.filters[
"bitwise_and"] = bitwise_and
2891 self.filters[
"bitwise_or"] = bitwise_or
2892 self.filters[
"bitwise_xor"] = bitwise_xor
2893 self.filters[
"pack"] = struct_pack
2894 self.filters[
"unpack"] = struct_unpack
2895 self.filters[
"ord"] = ord
2896 self.filters[
"is_number"] = is_number
2897 self.filters[
"float"] = forgiving_float_filter
2898 self.filters[
"int"] = forgiving_int_filter
2899 self.filters[
"slugify"] = slugify
2900 self.filters[
"iif"] = iif
2901 self.filters[
"bool"] = forgiving_boolean
2902 self.filters[
"version"] = version
2903 self.filters[
"contains"] = contains
2904 self.globals[
"log"] = logarithm
2905 self.globals[
"sin"] = sine
2906 self.globals[
"cos"] = cosine
2907 self.globals[
"tan"] = tangent
2908 self.globals[
"sqrt"] = square_root
2909 self.globals[
"pi"] = math.pi
2910 self.globals[
"tau"] = math.pi * 2
2911 self.globals[
"e"] = math.e
2912 self.globals[
"asin"] = arc_sine
2913 self.globals[
"acos"] = arc_cosine
2914 self.globals[
"atan"] = arc_tangent
2915 self.globals[
"atan2"] = arc_tangent2
2916 self.globals[
"float"] = forgiving_float
2917 self.globals[
"as_datetime"] = as_datetime
2918 self.globals[
"as_local"] = dt_util.as_local
2919 self.globals[
"as_timedelta"] = as_timedelta
2920 self.globals[
"as_timestamp"] = forgiving_as_timestamp
2921 self.globals[
"timedelta"] = timedelta
2922 self.globals[
"merge_response"] = merge_response
2923 self.globals[
"strptime"] = strptime
2924 self.globals[
"urlencode"] = urlencode
2925 self.globals[
"average"] = average
2926 self.globals[
"median"] = median
2927 self.globals[
"statistical_mode"] = statistical_mode
2930 self.globals[
"is_number"] = is_number
2931 self.globals[
"set"] = _to_set
2932 self.globals[
"tuple"] = _to_tuple
2933 self.globals[
"int"] = forgiving_int
2934 self.globals[
"pack"] = struct_pack
2935 self.globals[
"unpack"] = struct_unpack
2936 self.globals[
"slugify"] = slugify
2937 self.globals[
"iif"] = iif
2938 self.globals[
"bool"] = forgiving_boolean
2939 self.globals[
"version"] = version
2940 self.globals[
"zip"] = zip
2941 self.tests[
"is_number"] = is_number
2942 self.tests[
"list"] = _is_list
2943 self.tests[
"set"] = _is_set
2944 self.tests[
"tuple"] = _is_tuple
2945 self.tests[
"datetime"] = _is_datetime
2946 self.tests[
"string_like"] = _is_string_like
2947 self.tests[
"match"] = regex_match
2948 self.tests[
"search"] = regex_search
2949 self.tests[
"contains"] = contains
2961 def hassfunction[**_P, _R](
2962 func: Callable[Concatenate[HomeAssistant, _P], _R],
2963 jinja_context: Callable[
2964 [Callable[Concatenate[Any, _P], _R]],
2965 Callable[Concatenate[Any, _P], _R],
2967 ) -> Callable[Concatenate[Any, _P], _R]:
2968 """Wrap function that depend on hass."""
2971 def wrapper(_: Any, *args: _P.args, **kwargs: _P.kwargs) -> _R:
2972 return func(hass, *args, **kwargs)
2974 return jinja_context(wrapper)
2976 self.globals[
"device_entities"] = hassfunction(device_entities)
2977 self.filters[
"device_entities"] = self.globals[
"device_entities"]
2979 self.globals[
"device_attr"] = hassfunction(device_attr)
2980 self.filters[
"device_attr"] = self.globals[
"device_attr"]
2982 self.globals[
"config_entry_attr"] = hassfunction(config_entry_attr)
2983 self.filters[
"config_entry_attr"] = self.globals[
"config_entry_attr"]
2985 self.globals[
"is_device_attr"] = hassfunction(is_device_attr)
2986 self.tests[
"is_device_attr"] = hassfunction(is_device_attr, pass_eval_context)
2988 self.globals[
"config_entry_id"] = hassfunction(config_entry_id)
2989 self.filters[
"config_entry_id"] = self.globals[
"config_entry_id"]
2991 self.globals[
"device_id"] = hassfunction(device_id)
2992 self.filters[
"device_id"] = self.globals[
"device_id"]
2994 self.globals[
"issues"] = hassfunction(issues)
2996 self.globals[
"issue"] = hassfunction(issue)
2997 self.filters[
"issue"] = self.globals[
"issue"]
2999 self.globals[
"areas"] = hassfunction(areas)
3001 self.globals[
"area_id"] = hassfunction(area_id)
3002 self.filters[
"area_id"] = self.globals[
"area_id"]
3004 self.globals[
"area_name"] = hassfunction(area_name)
3005 self.filters[
"area_name"] = self.globals[
"area_name"]
3007 self.globals[
"area_entities"] = hassfunction(area_entities)
3008 self.filters[
"area_entities"] = self.globals[
"area_entities"]
3010 self.globals[
"area_devices"] = hassfunction(area_devices)
3011 self.filters[
"area_devices"] = self.globals[
"area_devices"]
3013 self.globals[
"floors"] = hassfunction(floors)
3014 self.filters[
"floors"] = self.globals[
"floors"]
3016 self.globals[
"floor_id"] = hassfunction(floor_id)
3017 self.filters[
"floor_id"] = self.globals[
"floor_id"]
3019 self.globals[
"floor_name"] = hassfunction(floor_name)
3020 self.filters[
"floor_name"] = self.globals[
"floor_name"]
3022 self.globals[
"floor_areas"] = hassfunction(floor_areas)
3023 self.filters[
"floor_areas"] = self.globals[
"floor_areas"]
3025 self.globals[
"integration_entities"] = hassfunction(integration_entities)
3026 self.filters[
"integration_entities"] = self.globals[
"integration_entities"]
3028 self.globals[
"labels"] = hassfunction(labels)
3029 self.filters[
"labels"] = self.globals[
"labels"]
3031 self.globals[
"label_id"] = hassfunction(label_id)
3032 self.filters[
"label_id"] = self.globals[
"label_id"]
3034 self.globals[
"label_name"] = hassfunction(label_name)
3035 self.filters[
"label_name"] = self.globals[
"label_name"]
3037 self.globals[
"label_areas"] = hassfunction(label_areas)
3038 self.filters[
"label_areas"] = self.globals[
"label_areas"]
3040 self.globals[
"label_devices"] = hassfunction(label_devices)
3041 self.filters[
"label_devices"] = self.globals[
"label_devices"]
3043 self.globals[
"label_entities"] = hassfunction(label_entities)
3044 self.filters[
"label_entities"] = self.globals[
"label_entities"]
3049 def unsupported(name: str) -> Callable[[], NoReturn]:
3050 def warn_unsupported(*args: Any, **kwargs: Any) -> NoReturn:
3052 f
"Use of '{name}' is not supported in limited templates"
3055 return warn_unsupported
3102 for glob
in hass_globals:
3103 self.globals[glob] = unsupported(glob)
3104 for filt
in hass_filters:
3105 self.filters[filt] = unsupported(filt)
3106 for test
in hass_tests:
3107 self.filters[test] = unsupported(test)
3110 self.globals[
"expand"] = hassfunction(expand)
3111 self.filters[
"expand"] = self.globals[
"expand"]
3112 self.globals[
"closest"] = hassfunction(closest)
3113 self.filters[
"closest"] = hassfunction(closest_filter)
3114 self.globals[
"distance"] = hassfunction(distance)
3115 self.globals[
"is_hidden_entity"] = hassfunction(is_hidden_entity)
3116 self.tests[
"is_hidden_entity"] = hassfunction(
3117 is_hidden_entity, pass_eval_context
3119 self.globals[
"is_state"] = hassfunction(is_state)
3120 self.tests[
"is_state"] = hassfunction(is_state, pass_eval_context)
3121 self.globals[
"is_state_attr"] = hassfunction(is_state_attr)
3122 self.tests[
"is_state_attr"] = hassfunction(is_state_attr, pass_eval_context)
3123 self.globals[
"state_attr"] = hassfunction(state_attr)
3124 self.filters[
"state_attr"] = self.globals[
"state_attr"]
3125 self.globals[
"states"] =
AllStates(hass)
3126 self.filters[
"states"] = self.globals[
"states"]
3128 self.filters[
"state_translated"] = self.globals[
"state_translated"]
3129 self.globals[
"has_value"] = hassfunction(has_value)
3130 self.filters[
"has_value"] = self.globals[
"has_value"]
3131 self.tests[
"has_value"] = hassfunction(has_value, pass_eval_context)
3132 self.globals[
"utcnow"] = hassfunction(utcnow)
3133 self.globals[
"now"] = hassfunction(now)
3134 self.globals[
"relative_time"] = hassfunction(relative_time)
3135 self.filters[
"relative_time"] = self.globals[
"relative_time"]
3136 self.globals[
"time_since"] = hassfunction(time_since)
3137 self.filters[
"time_since"] = self.globals[
"time_since"]
3138 self.globals[
"time_until"] = hassfunction(time_until)
3139 self.filters[
"time_until"] = self.globals[
"time_until"]
3140 self.globals[
"today_at"] = hassfunction(today_at)
3141 self.filters[
"today_at"] = self.globals[
"today_at"]
3144 """Test if callback is safe."""
3146 obj, (AllStates, StateTranslated)
3150 """Test if attribute is safe."""
3152 obj, (AllStates, DomainStates, TemplateState, LoopContext, AsyncLoopContext)
3154 return attr[0] !=
"_"
3156 if isinstance(obj, Namespace):
3164 source: str | jinja2.nodes.Template,
3165 name: str |
None =
None,
3166 filename: str |
None =
None,
3167 raw: Literal[
False] =
False,
3168 defer_init: bool =
False,
3174 source: str | jinja2.nodes.Template,
3175 name: str |
None =
None,
3176 filename: str |
None =
None,
3177 raw: Literal[
True] = ...,
3178 defer_init: bool =
False,
3183 source: str | jinja2.nodes.Template,
3184 name: str |
None =
None,
3185 filename: str |
None =
None,
3187 defer_init: bool =
False,
3188 ) -> CodeType | str:
3189 """Compile the template."""
3192 or filename
is not None
3194 or defer_init
is not False
3207 compiled = super().
compile(source)
3208 self.template_cache[source] = compiled
3213
None _collect_all_lifecycle(self)
str __call__(self, str entity_id, bool|object rounded=_SENTINEL, bool with_unit=False)
Generator[TemplateState] __iter__(self)
None __init__(self, HomeAssistant hass)
def __getattr__(self, name)
None __init__(self, HomeAssistant hass, str domain)
None _collect_domain_lifecycle(self)
None _collect_domain(self)
TemplateState|None __getattr__(self, str name)
Generator[TemplateState] __iter__(self)
None __init__(self, dict[str, str] sources)
tuple[str, str|None, Callable[[], bool]|None] get_source(self, jinja2.Environment environment, str template)
dict[str, str] sources(self)
None _freeze_static(self)
None __init__(self, Template template)
bool _filter_domains_and_entities(self, str entity_id)
bool _filter_lifecycle_domains(self, str entity_id)
bool _filter_entities(self, str entity_id)
str|None __call__(self, str entity_id)
None __init__(self, HomeAssistant hass)
None set_template(self, str template_str, str action)
None __exit__(self, type[BaseException]|None exc_type, BaseException|None exc_value, TracebackType|None traceback)
def is_safe_attribute(self, obj, attr, value)
CodeType compile(self, str|jinja2.nodes.Template source, str|None name=None, str|None filename=None, Literal[False] raw=False, bool defer_init=False)
def is_safe_callable(self, obj)
None __init__(self, HomeAssistant|None hass, bool|None limited=False, bool|None strict=False, Callable[[int, str], None]|None log_fn=None)
None __init__(self, HomeAssistant hass, bool collect, str entity_id)
bool __eq__(self, object other)
Any __getitem__(self, str item)
None _collect_state(self)
str format_state(self, bool rounded, bool with_unit)
str state_with_unit(self)
None __init__(self, HomeAssistant hass, str entity_id, bool collect=True)
None __init__(self, HomeAssistant hass, State state, bool collect=True)
Any render(self, TemplateVarsType variables=None, bool parse_result=True, bool limited=False, **Any kwargs)
Any async_render_with_possible_json_value(self, Any value, Any error_value=_SENTINEL, dict[str, Any]|None variables=None, bool parse_result=False)
RenderInfo async_render_to_info(self, TemplateVarsType variables=None, bool strict=False, Callable[[int, str], None]|None log_fn=None, **Any kwargs)
Any async_render(self, TemplateVarsType variables=None, bool parse_result=True, bool limited=False, bool strict=False, Callable[[int, str], None]|None log_fn=None, **Any kwargs)
TemplateEnvironment _env(self)
None __init__(self, str template, HomeAssistant|None hass=None)
def render_with_possible_json_value(self, value, error_value=_SENTINEL)
bool async_render_will_timeout(self, float timeout, TemplateVarsType variables=None, bool strict=False, Callable[[int, str], None]|None log_fn=None, **Any kwargs)
Any _parse_result(self, str render_result)
jinja2.Template _ensure_compiled(self, bool limited=False, bool strict=False, Callable[[int, str], None]|None log_fn=None)
None __init__(self, tuple value, *str|None render_result=None)
Self __new__(cls, tuple value, *str|None render_result=None)
None __init__(self, _AOSmithCoordinatorT coordinator, str junction_id)
list[_T] match(self, BluetoothServiceInfoBleak service_info)
str async_rounded_state(HomeAssistant hass, str entity_id, State state)
bool valid_entity_id(str entity_id)
bool valid_domain(str domain)
tuple[str, str] split_entity_id(str entity_id)
AreaRegistry async_get(HomeAssistant hass)
bool time(HomeAssistant hass, dt_time|str|None before=None, dt_time|str|None after=None, str|Container[str]|None weekday=None)
dict[str, EntityInfo] entity_sources(HomeAssistant hass)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
None report_usage(str what, *str|None breaks_in_ha_version=None, ReportBehavior core_behavior=ReportBehavior.ERROR, ReportBehavior core_integration_behavior=ReportBehavior.LOG, ReportBehavior custom_integration_behavior=ReportBehavior.LOG, set[str]|None exclude_integrations=None, str|None integration_domain=None, int level=logging.WARNING)
str to_json(Any value, bool ensure_ascii=False, bool pretty_print=False, bool sort_keys=False)
bool is_hidden_entity(HomeAssistant hass, str entity_id)
None async_load_custom_templates(HomeAssistant hass)
Iterable[str] integration_entities(HomeAssistant hass, str entry_name)
type gen_result_wrapper(type[dict|list|set] kls)
TemplateState|None _get_template_state_from_state(HomeAssistant hass, str entity_id, State|None state)
bool _is_datetime(Any value)
def closest_filter(hass, *args)
None _attach(HomeAssistant hass, Any obj)
def random_every_time(context, values)
Iterable[State] expand(HomeAssistant hass, *Any args)
Any time_until(HomeAssistant hass, Any|datetime value, int precision=1)
def timestamp_local(value, default=_SENTINEL)
Any statistical_mode(*Any args, Any default=_SENTINEL)
def add(value, amount, default=_SENTINEL)
Any device_attr(HomeAssistant hass, str device_or_entity_id, str attr_name)
type[jinja2.Undefined] make_logging_undefined(bool|None strict, Callable[[int, str], None]|None log_fn)
HassLoader _get_hass_loader(HomeAssistant hass)
Any config_entry_attr(HomeAssistant hass, str config_entry_id_, str attr_name)
def arc_tangent2(*args, default=_SENTINEL)
None _collect_state(HomeAssistant hass, str entity_id)
def regex_findall(value, find="", ignorecase=False)
Iterable[str|None] labels(HomeAssistant hass, Any lookup_value=None)
bool async_setup(HomeAssistant hass)
datetime now(HomeAssistant hass)
def sine(value, default=_SENTINEL)
TemplateState|None _get_state_if_valid(HomeAssistant hass, str entity_id)
bool is_device_attr(HomeAssistant hass, str device_or_entity_id, str attr_name, Any attr_value)
Any state_attr(HomeAssistant hass, str entity_id, str name)
def multiply(value, amount, default=_SENTINEL)
def bitwise_or(first_value, second_value)
State|TemplateState|None _resolve_state(HomeAssistant hass, Any entity_id_or_state)
def arc_sine(value, default=_SENTINEL)
Any render_complex(Any value, TemplateVarsType variables=None, bool limited=False, bool parse_result=True)
None _to_json_default(Any obj)
Generator[TemplateState] _state_generator(HomeAssistant hass, str|None domain)
Any relative_time(HomeAssistant hass, Any value)
str base64_encode(str value)
str|None area_id(HomeAssistant hass, str lookup_value)
bool _is_string_like(Any value)
bool is_state_attr(HomeAssistant hass, str entity_id, str name, Any value)
Any average(*Any args, Any default=_SENTINEL)
Iterable[str] device_entities(HomeAssistant hass, str _device_id)
Iterable[str] floor_areas(HomeAssistant hass, str floor_id_or_name)
def cosine(value, default=_SENTINEL)
str|None label_name(HomeAssistant hass, str lookup_value)
str|None area_name(HomeAssistant hass, str lookup_value)
set[Any] _to_set(Any value)
Iterable[str] area_devices(HomeAssistant hass, str area_id_or_name)
str|bytes base64_decode(str value, str|None encoding="utf-8")
Any _readonly(*Any args, **Any kwargs)
str|None _label_id_or_name(HomeAssistant hass, str label_id_or_name)
bool is_state(HomeAssistant hass, str entity_id, str|list[str] state)
str|None floor_name(HomeAssistant hass, str lookup_value)
def timestamp_custom(value, date_format=DATE_STR_FORMAT, local=True, default=_SENTINEL)
TemplateState _template_state(HomeAssistant hass, State state)
bool has_value(HomeAssistant hass, str entity_id)
bool is_template_string(str maybe_template)
Any min_max_from_filter(Any builtin_filter, str name)
str|None device_id(HomeAssistant hass, str entity_id_or_device_name)
Any median(*Any args, Any default=_SENTINEL)
dict[tuple[str, str], dict[str, Any]] issues(HomeAssistant hass)
bool is_complex(Any value)
TemplateState|None _get_state(HomeAssistant hass, str entity_id)
str _render_with_context(str template_str, jinja2.Template template, **Any kwargs)
def forgiving_int_filter(value, default=_SENTINEL, base=10)
None attach(HomeAssistant hass, Any obj)
def arc_cosine(value, default=_SENTINEL)
TemplateState _template_state_no_collect(HomeAssistant hass, State state)
def regex_match(value, find="", ignorecase=False)
def regex_search(value, find="", ignorecase=False)
str|None config_entry_id(HomeAssistant hass, str entity_id)
def forgiving_float_filter(value, default=_SENTINEL)
timedelta|None as_timedelta(str value)
def bitwise_and(first_value, second_value)
str|None floor_id(HomeAssistant hass, Any lookup_value)
bytes|None struct_pack(Any|None value, str format_string)
str|None label_id(HomeAssistant hass, Any lookup_value)
Any _cached_parse_result(str render_result)
Iterable[str|None] areas(HomeAssistant hass)
Iterable[str] area_entities(HomeAssistant hass, str area_id_or_name)
str _get_area_name(area_registry.AreaRegistry area_reg, str valid_area_id)
def slugify(value, separator="_")
def regex_findall_index(value, find="", index=0, ignorecase=False)
def bitwise_xor(first_value, second_value)
def square_root(value, default=_SENTINEL)
def regex_replace(value="", find="", replace="", ignorecase=False)
def arc_tangent(value, default=_SENTINEL)
datetime today_at(HomeAssistant hass, str time_str="")
bool _is_tuple(Any value)
def forgiving_int(value, default=_SENTINEL, base=10)
dict[str, Any]|None issue(HomeAssistant hass, str domain, str issue_id)
Iterable[str|None] floors(HomeAssistant hass)
def fail_when_undefined(value)
def strptime(string, fmt, default=_SENTINEL)
def logarithm(value, base=math.e, default=_SENTINEL)
dict[str, str] _load_custom_templates(HomeAssistant hass)
list[Any] merge_response(ServiceResponse value)
Any iif(Any value, Any if_true=True, Any if_false=False, Any if_none=_SENTINEL)
Any time_since(HomeAssistant hass, Any|datetime value, int precision=1)
def timestamp_utc(value, default=_SENTINEL)
_create_template_state_no_collect
def distance(hass, *args)
Iterable[str] label_devices(HomeAssistant hass, str label_id_or_name)
def forgiving_as_timestamp(value, default=_SENTINEL)
datetime utcnow(HomeAssistant hass)
Any|None struct_unpack(bytes value, str format_string, int offset=0)
bool result_as_boolean(Any|None template_result)
def forgiving_round(value, precision=0, method="common", default=_SENTINEL)
def tangent(value, default=_SENTINEL)
Any as_datetime(Any value, Any default=_SENTINEL)
bool|object forgiving_boolean(Any value)
Iterable[str] label_entities(HomeAssistant hass, str label_id_or_name)
NoReturn raise_no_default(str function, Any value)
Iterable[str] label_areas(HomeAssistant hass, str label_id_or_name)
DomainStates _domain_states(HomeAssistant hass, str name)
def forgiving_float(value, default=_SENTINEL)
str async_translate_state(HomeAssistant hass, str state, str domain, str|None platform, str|None translation_key, str|None device_class)