diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d4d9cf65354..fbd954a8103 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=hass,alot,datas,dof,dur,ether,farenheit,hist,iff,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa + - --ignore-words-list=hass,alot,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 2a605561572..6373f63c5a0 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1763,6 +1763,25 @@ def slugify(value, separator="_"): return slugify_util(value, separator=separator) +def iif( + value: Any, if_true: Any = True, if_false: Any = False, if_none: Any = _SENTINEL +) -> Any: + """Immediate if function/filter that allow for common if/else constructs. + + https://en.wikipedia.org/wiki/IIf + + Examples: + {{ is_state("device_tracker.frenck", "home") | iif("yes", "no") }} + {{ iif(1==2, "yes", "no") }} + {{ (1 == 1) | iif("yes", "no") }} + """ + if value is None and if_none is not _SENTINEL: + return if_none + if bool(value): + return if_true + return if_false + + @contextmanager def set_template(template_str: str, action: str) -> Generator: """Store template being parsed or rendered in a Contextvar to aid error handling.""" @@ -1877,6 +1896,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.filters["int"] = forgiving_int_filter self.filters["relative_time"] = relative_time self.filters["slugify"] = slugify + self.filters["iif"] = iif self.globals["log"] = logarithm self.globals["sin"] = sine self.globals["cos"] = cosine @@ -1906,6 +1926,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.globals["pack"] = struct_pack self.globals["unpack"] = struct_unpack self.globals["slugify"] = slugify + self.globals["iif"] = iif self.tests["match"] = regex_match self.tests["search"] = regex_search diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 42a725eaff5..471a5b2c486 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -21,6 +21,7 @@ from homeassistant.const import ( TEMP_CELSIUS, VOLUME_LITERS, ) +from homeassistant.core import HomeAssistant from homeassistant.exceptions import TemplateError from homeassistant.helpers import device_registry as dr, entity, template from homeassistant.helpers.entity_platform import EntityPlatform @@ -3101,6 +3102,38 @@ def test_urlencode(hass): assert tpl.async_render() == "the%20quick%20brown%20fox%20%3D%20true" +def test_iif(hass: HomeAssistant) -> None: + """Test the immediate if function/filter.""" + tpl = template.Template("{{ (1 == 1) | iif }}", hass) + assert tpl.async_render() is True + + tpl = template.Template("{{ (1 == 2) | iif }}", hass) + assert tpl.async_render() is False + + tpl = template.Template("{{ (1 == 1) | iif('yes') }}", hass) + assert tpl.async_render() == "yes" + + tpl = template.Template("{{ (1 == 2) | iif('yes') }}", hass) + assert tpl.async_render() is False + + tpl = template.Template("{{ (1 == 2) | iif('yes', 'no') }}", hass) + assert tpl.async_render() == "no" + + tpl = template.Template("{{ not_exists | default(None) | iif('yes', 'no') }}", hass) + assert tpl.async_render() == "no" + + tpl = template.Template( + "{{ not_exists | default(None) | iif('yes', 'no', 'unknown') }}", hass + ) + assert tpl.async_render() == "unknown" + + tpl = template.Template("{{ iif(1 == 1) }}", hass) + assert tpl.async_render() is True + + tpl = template.Template("{{ iif(1 == 2, 'yes', 'no') }}", hass) + assert tpl.async_render() == "no" + + async def test_cache_garbage_collection(): """Test caching a template.""" template_string = (