From 34e9c29ef229045aa74ad7d0aa7b382ddcc398a3 Mon Sep 17 00:00:00 2001 From: dougiteixeira <31328123+dougiteixeira@users.noreply.github.com> Date: Sun, 25 Feb 2024 23:56:52 -0300 Subject: [PATCH] Esphome text sensor device class (#111057) --- homeassistant/components/esphome/sensor.py | 24 +++++- tests/components/esphome/test_sensor.py | 96 +++++++++++++++++++++- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/sensor.py b/homeassistant/components/esphome/sensor.py index efc77ff53b8..d2be19a3fb3 100644 --- a/homeassistant/components/esphome/sensor.py +++ b/homeassistant/components/esphome/sensor.py @@ -1,7 +1,7 @@ """Support for esphome sensors.""" from __future__ import annotations -from datetime import datetime +from datetime import date, datetime import math from aioesphomeapi import ( @@ -106,9 +106,27 @@ class EsphomeSensor(EsphomeEntity[SensorInfo, SensorState], SensorEntity): class EsphomeTextSensor(EsphomeEntity[TextSensorInfo, TextSensorState], SensorEntity): """A text sensor implementation for ESPHome.""" + @callback + def _on_static_info_update(self, static_info: EntityInfo) -> None: + """Set attrs from static info.""" + super()._on_static_info_update(static_info) + static_info = self._static_info + self._attr_device_class = try_parse_enum( + SensorDeviceClass, static_info.device_class + ) + @property @esphome_state_property - def native_value(self) -> str | None: + def native_value(self) -> str | datetime | date | None: """Return the state of the entity.""" state = self._state - return None if state.missing_state else state.state + if state.missing_state: + return None + if self._attr_device_class is SensorDeviceClass.TIMESTAMP: + return dt_util.parse_datetime(state.state) + if ( + self._attr_device_class is SensorDeviceClass.DATE + and (value := dt_util.parse_datetime(state.state)) is not None + ): + return value.date() + return state.state diff --git a/tests/components/esphome/test_sensor.py b/tests/components/esphome/test_sensor.py index 080976425f9..a824a4e3905 100644 --- a/tests/components/esphome/test_sensor.py +++ b/tests/components/esphome/test_sensor.py @@ -17,8 +17,17 @@ from aioesphomeapi import ( UserService, ) -from homeassistant.components.sensor import ATTR_STATE_CLASS, SensorStateClass -from homeassistant.const import ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT, STATE_UNKNOWN +from homeassistant.components.sensor import ( + ATTR_STATE_CLASS, + SensorDeviceClass, + SensorStateClass, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_ICON, + ATTR_UNIT_OF_MEASUREMENT, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory @@ -320,6 +329,89 @@ async def test_generic_text_sensor( assert state.state == "i am a teapot" +async def test_generic_text_sensor_missing_state( + hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry +) -> None: + """Test a generic text sensor that is missing state.""" + entity_info = [ + TextSensorInfo( + object_id="mysensor", + key=1, + name="my sensor", + unique_id="my_sensor", + ) + ] + states = [TextSensorState(key=1, state=True, missing_state=True)] + user_service = [] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + user_service=user_service, + states=states, + ) + state = hass.states.get("sensor.test_mysensor") + assert state is not None + assert state.state == STATE_UNKNOWN + + +async def test_generic_text_sensor_device_class_timestamp( + hass: HomeAssistant, + mock_client: APIClient, + mock_generic_device_entry, +) -> None: + """Test a sensor entity that uses timestamp (datetime).""" + entity_info = [ + TextSensorInfo( + object_id="mysensor", + key=1, + name="my sensor", + unique_id="my_sensor", + device_class=SensorDeviceClass.TIMESTAMP, + ) + ] + states = [TextSensorState(key=1, state="2023-06-22T18:43:52+00:00")] + user_service = [] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + user_service=user_service, + states=states, + ) + state = hass.states.get("sensor.test_mysensor") + assert state is not None + assert state.state == "2023-06-22T18:43:52+00:00" + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TIMESTAMP + + +async def test_generic_text_sensor_device_class_date( + hass: HomeAssistant, + mock_client: APIClient, + mock_generic_device_entry, +) -> None: + """Test a sensor entity that uses date (datetime).""" + entity_info = [ + TextSensorInfo( + object_id="mysensor", + key=1, + name="my sensor", + unique_id="my_sensor", + device_class=SensorDeviceClass.DATE, + ) + ] + states = [TextSensorState(key=1, state="2023-06-22T18:43:52+00:00")] + user_service = [] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + user_service=user_service, + states=states, + ) + state = hass.states.get("sensor.test_mysensor") + assert state is not None + assert state.state == "2023-06-22" + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.DATE + + async def test_generic_numeric_sensor_empty_string_uom( hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry ) -> None: