From 14a1bb423c036a285f7570483fcb318910b2440f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 27 Sep 2021 10:47:57 +0200 Subject: [PATCH] Add is_number template filter and function (#56705) --- homeassistant/helpers/template.py | 13 ++++++++++++ tests/helpers/test_template.py | 35 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 3580af3e2bd..94323c14f56 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1370,6 +1370,17 @@ def forgiving_float(value): return value +def is_number(value): + """Try to convert value to a float.""" + try: + fvalue = float(value) + except (ValueError, TypeError): + return False + if math.isnan(fvalue) or math.isinf(fvalue): + return False + return True + + def regex_match(value, find="", ignorecase=False): """Match value using regex.""" if not isinstance(value, str): @@ -1575,6 +1586,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.filters["bitwise_and"] = bitwise_and self.filters["bitwise_or"] = bitwise_or self.filters["ord"] = ord + self.filters["is_number"] = is_number self.globals["log"] = logarithm self.globals["sin"] = sine self.globals["cos"] = cosine @@ -1597,6 +1609,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.globals["urlencode"] = urlencode self.globals["max"] = max self.globals["min"] = min + self.globals["is_number"] = is_number 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 efdcebf70e1..0ac59c68d2d 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -220,6 +220,41 @@ def test_float(hass): ) +@pytest.mark.parametrize( + "value, expected", + [ + (0, True), + (0.0, True), + ("0", True), + ("0.0", True), + (True, True), + (False, True), + ("True", False), + ("False", False), + (None, False), + ("None", False), + ("horse", False), + (math.pi, True), + (math.nan, False), + (math.inf, False), + ("nan", False), + ("inf", False), + ], +) +def test_isnumber(hass, value, expected): + """Test is_number.""" + assert ( + template.Template("{{ is_number(value) }}", hass).async_render({"value": value}) + == expected + ) + assert ( + template.Template("{{ value | is_number }}", hass).async_render( + {"value": value} + ) + == expected + ) + + def test_rounding_value(hass): """Test rounding value.""" hass.states.async_set("sensor.temperature", 12.78)