diff --git a/homeassistant/components/template/sensor.py b/homeassistant/components/template/sensor.py index b10dc6be443..1b3ac858c4c 100644 --- a/homeassistant/components/template/sensor.py +++ b/homeassistant/components/template/sensor.py @@ -256,6 +256,9 @@ class AbstractTemplateSensor(AbstractTemplateEntity, RestoreSensor): self, result: Any ) -> StateType | date | datetime | Decimal | None: """Validate the state.""" + if self._numeric_state_expected: + return template_validators.number(self, CONF_STATE)(result) + if result is None or self.device_class not in ( SensorDeviceClass.DATE, SensorDeviceClass.TIMESTAMP, diff --git a/tests/components/template/test_config_flow.py b/tests/components/template/test_config_flow.py index 5e0f37d59c2..59de6a3d28a 100644 --- a/tests/components/template/test_config_flow.py +++ b/tests/components/template/test_config_flow.py @@ -1306,7 +1306,7 @@ async def test_config_flow_preview_template_error( [ ( "sensor", - "{{ states('sensor.one') }}", + "{{ 1.0 / states('sensor.one') | float(0.0) }}", {"unit_of_measurement": "°C"}, ), ], @@ -1350,14 +1350,7 @@ async def test_config_flow_preview_bad_state( assert msg["result"] is None msg = await client.receive_json() - assert msg["event"] == { - "error": ( - "Sensor None has device class 'None', state class 'None' unit '°C' " - "and suggested precision 'None' thus indicating it has a numeric " - "value; however, it has the non-numeric value: 'unknown' ()" - ), - } + assert msg["event"] == {"error": "ZeroDivisionError: division by zero"} @pytest.mark.parametrize( diff --git a/tests/components/template/test_sensor.py b/tests/components/template/test_sensor.py index 0c5688ae88c..ffb802527bf 100644 --- a/tests/components/template/test_sensor.py +++ b/tests/components/template/test_sensor.py @@ -692,7 +692,7 @@ async def test_sun_renders_once_per_sensor(hass: HomeAssistant) -> None: hass.states.async_set("sun.sun", {"elevation": 50, "next_rising": later}) await hass.async_block_till_done() - assert hass.states.get("sensor.solar_angle").state == "75" + assert hass.states.get("sensor.solar_angle").state == "75.0" assert hass.states.get("sensor.sunrise").state == "75" assert len(async_render_calls) == 2 @@ -1524,7 +1524,7 @@ async def test_last_reset(hass: HomeAssistant, expected: str) -> None: state = hass.states.get(TEST_SENSOR.entity_id) assert state is not None - assert state.state == "0" + assert state.state == "0.0" assert state.attributes["state_class"] == "total" assert state.attributes["last_reset"] == expected @@ -1553,7 +1553,7 @@ async def test_invalid_last_reset( state = hass.states.get(TEST_SENSOR.entity_id) assert state is not None - assert state.state == "0" + assert state.state == "0.0" assert state.attributes.get("last_reset") is None err = "Received invalid sensor last_reset: not a datetime for entity" @@ -1956,3 +1956,40 @@ async def test_flow_preview( ) assert state["state"] == "0.0" + + +@pytest.mark.parametrize( + ("count", "config", "state_template"), + [ + ( + 1, + { + "device_class": "temperature", + "state_class": "measurement", + "unit_of_measurement": "°C", + }, + "{{ states('sensor.test_state') }}", + ) + ], +) +@pytest.mark.parametrize( + "style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER] +) +@pytest.mark.usefixtures("setup_state_sensor") +async def test_numeric_sensor_recovers_from_exception(hass: HomeAssistant) -> None: + """Test template.""" + assert hass.states.get(TEST_SENSOR.entity_id).state == STATE_UNKNOWN + + for set_state, expected_state in ( + ("0.0", "0.0"), + ("unavailable", STATE_UNKNOWN), + ("1.0", "1.0"), + ("unknown", STATE_UNKNOWN), + ("2.0", "2.0"), + ("kjfdah", STATE_UNKNOWN), + ("3.0", "3.0"), + ("3.x", STATE_UNKNOWN), + ("4.0", "4.0"), + ): + await async_trigger(hass, TEST_STATE_SENSOR, set_state) + assert hass.states.get(TEST_SENSOR.entity_id).state == expected_state