Add enum device_class and state options property to sensor entities (#82489)
This commit is contained in:
parent
3feea879dd
commit
ce00f093d6
9 changed files with 272 additions and 8 deletions
|
@ -126,6 +126,17 @@ async def async_setup_platform(
|
||||||
None,
|
None,
|
||||||
"total_gas_ft3",
|
"total_gas_ft3",
|
||||||
),
|
),
|
||||||
|
DemoSensor(
|
||||||
|
unique_id="sensor_10",
|
||||||
|
name="Thermostat mode",
|
||||||
|
state="eco",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
state_class=None,
|
||||||
|
unit_of_measurement=None,
|
||||||
|
battery=None,
|
||||||
|
options=["away", "comfort", "eco", "sleep"],
|
||||||
|
translation_key="thermostat_mode",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -149,10 +160,12 @@ class DemoSensor(SensorEntity):
|
||||||
unique_id: str,
|
unique_id: str,
|
||||||
name: str,
|
name: str,
|
||||||
state: StateType,
|
state: StateType,
|
||||||
device_class: SensorDeviceClass,
|
device_class: SensorDeviceClass | str,
|
||||||
state_class: SensorStateClass | None,
|
state_class: SensorStateClass | None,
|
||||||
unit_of_measurement: str | None,
|
unit_of_measurement: str | None,
|
||||||
battery: StateType,
|
battery: StateType,
|
||||||
|
options: list[str] | None = None,
|
||||||
|
translation_key: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self._attr_device_class = device_class
|
self._attr_device_class = device_class
|
||||||
|
@ -161,6 +174,8 @@ class DemoSensor(SensorEntity):
|
||||||
self._attr_native_value = state
|
self._attr_native_value = state
|
||||||
self._attr_state_class = state_class
|
self._attr_state_class = state_class
|
||||||
self._attr_unique_id = unique_id
|
self._attr_unique_id = unique_id
|
||||||
|
self._attr_options = options
|
||||||
|
self._attr_translation_key = translation_key
|
||||||
|
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, unique_id)},
|
identifiers={(DOMAIN, unique_id)},
|
||||||
|
|
|
@ -61,5 +61,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"thermostat_mode": {
|
||||||
|
"state": {
|
||||||
|
"away": "Away",
|
||||||
|
"comfort": "Comfort",
|
||||||
|
"eco": "Eco",
|
||||||
|
"sleep": "Sleep"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
{
|
{
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"thermostat_mode": {
|
||||||
|
"state": {
|
||||||
|
"away": "Away",
|
||||||
|
"comfort": "Comfort",
|
||||||
|
"eco": "Eco",
|
||||||
|
"sleep": "Sleep"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
"bad_psu": {
|
"bad_psu": {
|
||||||
"fix_flow": {
|
"fix_flow": {
|
||||||
|
@ -15,7 +27,8 @@
|
||||||
"fix_flow": {
|
"fix_flow": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"not_tea_time": "Can not re-heat the tea at this time"
|
"not_tea_time": "Can not re-heat the tea at this time"
|
||||||
}
|
},
|
||||||
|
"step": {}
|
||||||
},
|
},
|
||||||
"title": "The tea is cold"
|
"title": "The tea is cold"
|
||||||
},
|
},
|
||||||
|
@ -41,6 +54,9 @@
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {}
|
||||||
|
},
|
||||||
"options_1": {
|
"options_1": {
|
||||||
"data": {
|
"data": {
|
||||||
"bool": "Optional boolean",
|
"bool": "Optional boolean",
|
||||||
|
|
|
@ -77,6 +77,7 @@ _LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_LAST_RESET: Final = "last_reset"
|
ATTR_LAST_RESET: Final = "last_reset"
|
||||||
ATTR_STATE_CLASS: Final = "state_class"
|
ATTR_STATE_CLASS: Final = "state_class"
|
||||||
|
ATTR_OPTIONS: Final = "options"
|
||||||
|
|
||||||
DOMAIN: Final = "sensor"
|
DOMAIN: Final = "sensor"
|
||||||
|
|
||||||
|
@ -103,6 +104,14 @@ class SensorDeviceClass(StrEnum):
|
||||||
Unit of measurement: `d`, `h`, `min`, `s`
|
Unit of measurement: `d`, `h`, `min`, `s`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ENUM = "enum"
|
||||||
|
"""Enumeration.
|
||||||
|
|
||||||
|
Provides a fixed list of options the state of the sensor can be in.
|
||||||
|
|
||||||
|
Unit of measurement: `None`
|
||||||
|
"""
|
||||||
|
|
||||||
TIMESTAMP = "timestamp"
|
TIMESTAMP = "timestamp"
|
||||||
"""Timestamp.
|
"""Timestamp.
|
||||||
|
|
||||||
|
@ -446,6 +455,7 @@ class SensorEntityDescription(EntityDescription):
|
||||||
last_reset: datetime | None = None
|
last_reset: datetime | None = None
|
||||||
native_unit_of_measurement: str | None = None
|
native_unit_of_measurement: str | None = None
|
||||||
state_class: SensorStateClass | str | None = None
|
state_class: SensorStateClass | str | None = None
|
||||||
|
options: list[str] | None = None
|
||||||
unit_of_measurement: None = None # Type override, use native_unit_of_measurement
|
unit_of_measurement: None = None # Type override, use native_unit_of_measurement
|
||||||
|
|
||||||
|
|
||||||
|
@ -457,6 +467,7 @@ class SensorEntity(Entity):
|
||||||
_attr_last_reset: datetime | None
|
_attr_last_reset: datetime | None
|
||||||
_attr_native_unit_of_measurement: str | None
|
_attr_native_unit_of_measurement: str | None
|
||||||
_attr_native_value: StateType | date | datetime | Decimal = None
|
_attr_native_value: StateType | date | datetime | Decimal = None
|
||||||
|
_attr_options: list[str] | None
|
||||||
_attr_state_class: SensorStateClass | str | None
|
_attr_state_class: SensorStateClass | str | None
|
||||||
_attr_state: None = None # Subclasses of SensorEntity should not set this
|
_attr_state: None = None # Subclasses of SensorEntity should not set this
|
||||||
_attr_suggested_unit_of_measurement: str | None
|
_attr_suggested_unit_of_measurement: str | None
|
||||||
|
@ -523,6 +534,15 @@ class SensorEntity(Entity):
|
||||||
return self.entity_description.device_class
|
return self.entity_description.device_class
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self) -> list[str] | None:
|
||||||
|
"""Return a set of possible options."""
|
||||||
|
if hasattr(self, "_attr_options"):
|
||||||
|
return self._attr_options
|
||||||
|
if hasattr(self, "entity_description"):
|
||||||
|
return self.entity_description.options
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_class(self) -> SensorStateClass | str | None:
|
def state_class(self) -> SensorStateClass | str | None:
|
||||||
"""Return the state class of this entity, if any."""
|
"""Return the state class of this entity, if any."""
|
||||||
|
@ -547,6 +567,9 @@ class SensorEntity(Entity):
|
||||||
if state_class := self.state_class:
|
if state_class := self.state_class:
|
||||||
return {ATTR_STATE_CLASS: state_class}
|
return {ATTR_STATE_CLASS: state_class}
|
||||||
|
|
||||||
|
if options := self.options:
|
||||||
|
return {ATTR_OPTIONS: options}
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_initial_suggested_unit(self) -> str | None:
|
def _get_initial_suggested_unit(self) -> str | None:
|
||||||
|
@ -679,6 +702,7 @@ class SensorEntity(Entity):
|
||||||
unit_of_measurement = self.unit_of_measurement
|
unit_of_measurement = self.unit_of_measurement
|
||||||
value = self.native_value
|
value = self.native_value
|
||||||
device_class = self.device_class
|
device_class = self.device_class
|
||||||
|
state_class = self.state_class
|
||||||
|
|
||||||
# Received a datetime
|
# Received a datetime
|
||||||
if value is not None and device_class == DEVICE_CLASS_TIMESTAMP:
|
if value is not None and device_class == DEVICE_CLASS_TIMESTAMP:
|
||||||
|
@ -715,6 +739,37 @@ class SensorEntity(Entity):
|
||||||
f"but provides state {value}:{type(value)} resulting in '{err}'"
|
f"but provides state {value}:{type(value)} resulting in '{err}'"
|
||||||
) from err
|
) from err
|
||||||
|
|
||||||
|
# Enum checks
|
||||||
|
if value is not None and (
|
||||||
|
device_class == SensorDeviceClass.ENUM or self.options is not None
|
||||||
|
):
|
||||||
|
if device_class != SensorDeviceClass.ENUM:
|
||||||
|
reason = "is missing the enum device class"
|
||||||
|
if device_class is not None:
|
||||||
|
reason = f"has device class '{device_class}' instead of 'enum'"
|
||||||
|
raise ValueError(
|
||||||
|
f"Sensor {self.entity_id} is providing enum options, but {reason}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if state_class:
|
||||||
|
raise ValueError(
|
||||||
|
f"Sensor {self.entity_id} has an state_class and thus indicating "
|
||||||
|
"it has a numeric value; however, it has the enum device class"
|
||||||
|
)
|
||||||
|
|
||||||
|
if unit_of_measurement:
|
||||||
|
raise ValueError(
|
||||||
|
f"Sensor {self.entity_id} has an unit of measurement and thus "
|
||||||
|
"indicating it has a numeric value; "
|
||||||
|
"however, it has the enum device class"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (options := self.options) and value not in options:
|
||||||
|
raise ValueError(
|
||||||
|
f"Sensor {self.entity_id} provides state value '{value}', "
|
||||||
|
"which is not in the list of options provided"
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
value is not None
|
value is not None
|
||||||
and native_unit_of_measurement != unit_of_measurement
|
and native_unit_of_measurement != unit_of_measurement
|
||||||
|
@ -840,7 +895,7 @@ class SensorExtraStoredData(ExtraStoredData):
|
||||||
# native_value is a dict, but does not have all values
|
# native_value is a dict, but does not have all values
|
||||||
return None
|
return None
|
||||||
except DecimalInvalidOperation:
|
except DecimalInvalidOperation:
|
||||||
# native_value coulnd't be returned from decimal_str
|
# native_value couldn't be returned from decimal_str
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return cls(native_value, native_unit_of_measurement)
|
return cls(native_value, native_unit_of_measurement)
|
||||||
|
|
|
@ -29,13 +29,14 @@ from homeassistant.const import (
|
||||||
VOLUME_CUBIC_FEET,
|
VOLUME_CUBIC_FEET,
|
||||||
VOLUME_CUBIC_METERS,
|
VOLUME_CUBIC_METERS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, State, split_entity_id
|
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity import entity_sources
|
from homeassistant.helpers.entity import entity_sources
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_LAST_RESET,
|
ATTR_LAST_RESET,
|
||||||
|
ATTR_OPTIONS,
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
@ -724,3 +725,9 @@ def validate_statistics(
|
||||||
)
|
)
|
||||||
|
|
||||||
return validation_result
|
return validation_result
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def exclude_attributes(hass: HomeAssistant) -> set[str]:
|
||||||
|
"""Exclude attributes from being recorded in the database."""
|
||||||
|
return {ATTR_OPTIONS}
|
||||||
|
|
|
@ -857,6 +857,7 @@ def test_device_classes_aligned():
|
||||||
non_numeric_device_classes = {
|
non_numeric_device_classes = {
|
||||||
SensorDeviceClass.DATE,
|
SensorDeviceClass.DATE,
|
||||||
SensorDeviceClass.DURATION,
|
SensorDeviceClass.DURATION,
|
||||||
|
SensorDeviceClass.ENUM,
|
||||||
SensorDeviceClass.TIMESTAMP,
|
SensorDeviceClass.TIMESTAMP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
||||||
from pytest import approx
|
from pytest import approx
|
||||||
|
|
||||||
from homeassistant.components.number import NumberDeviceClass
|
from homeassistant.components.number import NumberDeviceClass
|
||||||
from homeassistant.components.sensor import SensorDeviceClass
|
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
LENGTH_CENTIMETERS,
|
LENGTH_CENTIMETERS,
|
||||||
|
@ -32,8 +32,9 @@ from homeassistant.const import (
|
||||||
VOLUME_CUBIC_METERS,
|
VOLUME_CUBIC_METERS,
|
||||||
VOLUME_FLUID_OUNCE,
|
VOLUME_FLUID_OUNCE,
|
||||||
VOLUME_LITERS,
|
VOLUME_LITERS,
|
||||||
|
UnitOfTemperature,
|
||||||
)
|
)
|
||||||
from homeassistant.core import State
|
from homeassistant.core import HomeAssistant, State
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY
|
from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@ -936,3 +937,124 @@ def test_device_classes_aligned():
|
||||||
for device_class in NumberDeviceClass:
|
for device_class in NumberDeviceClass:
|
||||||
assert hasattr(SensorDeviceClass, device_class.name)
|
assert hasattr(SensorDeviceClass, device_class.name)
|
||||||
assert getattr(SensorDeviceClass, device_class.name).value == device_class.value
|
assert getattr(SensorDeviceClass, device_class.name).value == device_class.value
|
||||||
|
|
||||||
|
|
||||||
|
async def test_value_unknown_in_enumeration(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
):
|
||||||
|
"""Test warning on invalid enum value."""
|
||||||
|
platform = getattr(hass.components, "test.sensor")
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES["0"] = platform.MockSensor(
|
||||||
|
name="Test",
|
||||||
|
native_value="invalid_option",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
options=["option1", "option2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Sensor sensor.test provides state value 'invalid_option', "
|
||||||
|
"which is not in the list of options provided"
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_enumeration_entity_with_device_class(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
):
|
||||||
|
"""Test warning on entities that provide an enum with a device class."""
|
||||||
|
platform = getattr(hass.components, "test.sensor")
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES["0"] = platform.MockSensor(
|
||||||
|
name="Test",
|
||||||
|
native_value=21,
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
options=["option1", "option2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Sensor sensor.test is providing enum options, but has device class 'power' "
|
||||||
|
"instead of 'enum'"
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_enumeration_entity_without_device_class(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
):
|
||||||
|
"""Test warning on entities that provide an enum without a device class."""
|
||||||
|
platform = getattr(hass.components, "test.sensor")
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES["0"] = platform.MockSensor(
|
||||||
|
name="Test",
|
||||||
|
native_value=21,
|
||||||
|
options=["option1", "option2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Sensor sensor.test is providing enum options, but is missing "
|
||||||
|
"the enum device class"
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_enumeration_with_state_class(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
):
|
||||||
|
"""Test warning on numeric entities that provide an enum."""
|
||||||
|
platform = getattr(hass.components, "test.sensor")
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES["0"] = platform.MockSensor(
|
||||||
|
name="Test",
|
||||||
|
native_value=42,
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
options=["option1", "option2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Sensor sensor.test has an state_class and thus indicating "
|
||||||
|
"it has a numeric value; however, it has the enum device class"
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_enumeration_with_unit_of_measurement(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
):
|
||||||
|
"""Test warning on numeric entities that provide an enum."""
|
||||||
|
platform = getattr(hass.components, "test.sensor")
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES["0"] = platform.MockSensor(
|
||||||
|
name="Test",
|
||||||
|
native_value=42,
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
options=["option1", "option2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Sensor sensor.test has an unit of measurement and thus indicating "
|
||||||
|
"it has a numeric value; however, it has the enum device class"
|
||||||
|
) in caplog.text
|
||||||
|
|
|
@ -10,7 +10,11 @@ from pytest import approx
|
||||||
|
|
||||||
from homeassistant import loader
|
from homeassistant import loader
|
||||||
from homeassistant.components.recorder import DOMAIN as RECORDER_DOMAIN, history
|
from homeassistant.components.recorder import DOMAIN as RECORDER_DOMAIN, history
|
||||||
from homeassistant.components.recorder.db_schema import StatisticsMeta
|
from homeassistant.components.recorder.db_schema import (
|
||||||
|
StateAttributes,
|
||||||
|
States,
|
||||||
|
StatisticsMeta,
|
||||||
|
)
|
||||||
from homeassistant.components.recorder.models import (
|
from homeassistant.components.recorder.models import (
|
||||||
StatisticData,
|
StatisticData,
|
||||||
StatisticMetaData,
|
StatisticMetaData,
|
||||||
|
@ -22,11 +26,14 @@ from homeassistant.components.recorder.statistics import (
|
||||||
list_statistic_ids,
|
list_statistic_ids,
|
||||||
)
|
)
|
||||||
from homeassistant.components.recorder.util import get_instance, session_scope
|
from homeassistant.components.recorder.util import get_instance, session_scope
|
||||||
from homeassistant.const import STATE_UNAVAILABLE
|
from homeassistant.components.sensor import ATTR_OPTIONS, DOMAIN
|
||||||
|
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE
|
||||||
|
from homeassistant.core import HomeAssistant, State
|
||||||
from homeassistant.setup import async_setup_component, setup_component
|
from homeassistant.setup import async_setup_component, setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM
|
from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM
|
||||||
|
|
||||||
|
from tests.common import async_fire_time_changed
|
||||||
from tests.components.recorder.common import (
|
from tests.components.recorder.common import (
|
||||||
async_recorder_block_till_done,
|
async_recorder_block_till_done,
|
||||||
async_wait_recording_done,
|
async_wait_recording_done,
|
||||||
|
@ -4320,3 +4327,27 @@ def record_states_partially_unavailable(hass, zero, entity_id, attributes):
|
||||||
)
|
)
|
||||||
|
|
||||||
return four, states
|
return four, states
|
||||||
|
|
||||||
|
|
||||||
|
async def test_exclude_attributes(recorder_mock: None, hass: HomeAssistant) -> None:
|
||||||
|
"""Test sensor attributes to be excluded."""
|
||||||
|
await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "demo"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
|
def _fetch_states() -> list[State]:
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
native_states = []
|
||||||
|
for db_state, db_state_attributes in session.query(States, StateAttributes):
|
||||||
|
state = db_state.to_native()
|
||||||
|
state.attributes = db_state_attributes.to_native()
|
||||||
|
native_states.append(state)
|
||||||
|
return native_states
|
||||||
|
|
||||||
|
states: list[State] = await hass.async_add_executor_job(_fetch_states)
|
||||||
|
assert len(states) > 1
|
||||||
|
for state in states:
|
||||||
|
assert ATTR_OPTIONS not in state.attributes
|
||||||
|
assert ATTR_FRIENDLY_NAME in state.attributes
|
||||||
|
|
|
@ -107,6 +107,11 @@ class MockSensor(MockEntity, SensorEntity):
|
||||||
"""Return the native value of this sensor."""
|
"""Return the native value of this sensor."""
|
||||||
return self._handle("native_value")
|
return self._handle("native_value")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self):
|
||||||
|
"""Return the options for this sensor."""
|
||||||
|
return self._handle("options")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_class(self):
|
def state_class(self):
|
||||||
"""Return the state class of this sensor."""
|
"""Return the state class of this sensor."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue