diff --git a/homeassistant/components/command_line/sensor.py b/homeassistant/components/command_line/sensor.py index dfc31b4581b..234241fdeab 100644 --- a/homeassistant/components/command_line/sensor.py +++ b/homeassistant/components/command_line/sensor.py @@ -10,8 +10,6 @@ from typing import Any from jsonpath import jsonpath -from homeassistant.components.sensor import SensorDeviceClass -from homeassistant.components.sensor.helpers import async_parse_date_datetime from homeassistant.const import ( CONF_COMMAND, CONF_NAME, @@ -188,16 +186,7 @@ class CommandSensor(ManualTriggerSensorEntity): self.entity_id, variables, None ) - if self.device_class not in { - SensorDeviceClass.DATE, - SensorDeviceClass.TIMESTAMP, - }: - self._attr_native_value = value - elif value is not None: - self._attr_native_value = async_parse_date_datetime( - value, self.entity_id, self.device_class - ) - + self._set_native_value_with_possible_timestamp(value) self._process_manual_data(variables) self.async_write_ha_state() diff --git a/homeassistant/components/rest/sensor.py b/homeassistant/components/rest/sensor.py index 9df10197a1a..3db44b0e5d2 100644 --- a/homeassistant/components/rest/sensor.py +++ b/homeassistant/components/rest/sensor.py @@ -13,9 +13,7 @@ from homeassistant.components.sensor import ( CONF_STATE_CLASS, DOMAIN as SENSOR_DOMAIN, PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA, - SensorDeviceClass, ) -from homeassistant.components.sensor.helpers import async_parse_date_datetime from homeassistant.const import ( CONF_DEVICE_CLASS, CONF_FORCE_UPDATE, @@ -181,18 +179,6 @@ class RestSensor(ManualTriggerSensorEntity, RestEntity): self.entity_id, variables, None ) - if value is None or self.device_class not in ( - SensorDeviceClass.DATE, - SensorDeviceClass.TIMESTAMP, - ): - self._attr_native_value = value - self._process_manual_data(variables) - self.async_write_ha_state() - return - - self._attr_native_value = async_parse_date_datetime( - value, self.entity_id, self.device_class - ) - + self._set_native_value_with_possible_timestamp(value) self._process_manual_data(variables) self.async_write_ha_state() diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 80d53a2c8b1..3e7f416166b 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -7,8 +7,7 @@ from typing import Any, cast import voluptuous as vol -from homeassistant.components.sensor import CONF_STATE_CLASS, SensorDeviceClass -from homeassistant.components.sensor.helpers import async_parse_date_datetime +from homeassistant.components.sensor import CONF_STATE_CLASS from homeassistant.const import ( CONF_ATTRIBUTE, CONF_DEVICE_CLASS, @@ -218,17 +217,7 @@ class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], ManualTriggerSensorEnti self.entity_id, variables, None ) - if self.device_class not in { - SensorDeviceClass.DATE, - SensorDeviceClass.TIMESTAMP, - }: - self._attr_native_value = value - self._process_manual_data(variables) - return - - self._attr_native_value = async_parse_date_datetime( - value, self.entity_id, self.device_class - ) + self._set_native_value_with_possible_timestamp(value) self._process_manual_data(variables) @property diff --git a/homeassistant/components/snmp/sensor.py b/homeassistant/components/snmp/sensor.py index 3574affaccd..46e0dc83050 100644 --- a/homeassistant/components/snmp/sensor.py +++ b/homeassistant/components/snmp/sensor.py @@ -217,7 +217,7 @@ class SnmpSensor(ManualTriggerSensorEntity): self.entity_id, variables, STATE_UNKNOWN ) - self._attr_native_value = value + self._set_native_value_with_possible_timestamp(value) self._process_manual_data(variables) diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index b86a33db7ab..8c0ba81d6d2 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -401,9 +401,10 @@ class SQLSensor(ManualTriggerSensorEntity): if data is not None and self._template is not None: variables = self._template_variables_with_value(data) if self._render_availability_template(variables): - self._attr_native_value = self._template.async_render_as_value_template( + _value = self._template.async_render_as_value_template( self.entity_id, variables, None ) + self._set_native_value_with_possible_timestamp(_value) self._process_manual_data(variables) else: self._attr_native_value = data diff --git a/homeassistant/helpers/trigger_template_entity.py b/homeassistant/helpers/trigger_template_entity.py index bf7598eb024..d8ebab8b83e 100644 --- a/homeassistant/helpers/trigger_template_entity.py +++ b/homeassistant/helpers/trigger_template_entity.py @@ -13,8 +13,10 @@ from homeassistant.components.sensor import ( CONF_STATE_CLASS, DEVICE_CLASSES_SCHEMA, STATE_CLASSES_SCHEMA, + SensorDeviceClass, SensorEntity, ) +from homeassistant.components.sensor.helpers import async_parse_date_datetime from homeassistant.const import ( ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME, @@ -389,3 +391,20 @@ class ManualTriggerSensorEntity(ManualTriggerEntity, SensorEntity): ManualTriggerEntity.__init__(self, hass, config) self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT) self._attr_state_class = config.get(CONF_STATE_CLASS) + + @callback + def _set_native_value_with_possible_timestamp(self, value: Any) -> None: + """Set native value with possible timestamp. + + If self.device_class is `date` or `timestamp`, + it will try to parse the value to a date/datetime object. + """ + if self.device_class not in ( + SensorDeviceClass.DATE, + SensorDeviceClass.TIMESTAMP, + ): + self._attr_native_value = value + elif value is not None: + self._attr_native_value = async_parse_date_datetime( + value, self.entity_id, self.device_class + ) diff --git a/tests/helpers/test_trigger_template_entity.py b/tests/helpers/test_trigger_template_entity.py index 8389218054d..fcfdd249d75 100644 --- a/tests/helpers/test_trigger_template_entity.py +++ b/tests/helpers/test_trigger_template_entity.py @@ -4,7 +4,10 @@ from typing import Any import pytest +from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.components.sensor.helpers import async_parse_date_datetime from homeassistant.const import ( + CONF_DEVICE_CLASS, CONF_ICON, CONF_NAME, CONF_STATE, @@ -20,6 +23,7 @@ from homeassistant.helpers.trigger_template_entity import ( CONF_AVAILABILITY, CONF_PICTURE, ManualTriggerEntity, + ManualTriggerSensorEntity, ValueTemplate, ) @@ -288,3 +292,38 @@ async def test_trigger_template_complex(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert entity.some_other_key == {"test_key": "test_data"} + + +async def test_manual_trigger_sensor_entity_with_date( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test manual trigger template entity when availability template isn't used.""" + config = { + CONF_NAME: template.Template("test_entity", hass), + CONF_STATE: template.Template("{{ as_datetime(value) }}", hass), + CONF_DEVICE_CLASS: SensorDeviceClass.TIMESTAMP, + } + + class TestEntity(ManualTriggerSensorEntity): + """Test entity class.""" + + extra_template_keys = (CONF_STATE,) + + @property + def state(self) -> bool | None: + """Return extra attributes.""" + return "2025-01-01T00:00:00+00:00" + + entity = TestEntity(hass, config) + entity.entity_id = "test.entity" + variables = entity._template_variables_with_value("2025-01-01T00:00:00+00:00") + assert entity._render_availability_template(variables) is True + assert entity.available is True + entity._set_native_value_with_possible_timestamp(entity.state) + await hass.async_block_till_done() + + assert entity.native_value == async_parse_date_datetime( + "2025-01-01T00:00:00+00:00", entity.entity_id, entity.device_class + ) + assert entity.state == "2025-01-01T00:00:00+00:00" + assert entity.device_class == SensorDeviceClass.TIMESTAMP