Move sensor rounding to frontend (#87330)

* Move sensor rounding to frontend

* Update integrations

* Add comment
This commit is contained in:
Erik Montnemery 2023-02-04 20:54:36 +01:00 committed by GitHub
parent ee6773236e
commit bcc1aa03b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 231 additions and 307 deletions

View file

@ -249,7 +249,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
metric_unit=UnitOfLength.METERS, metric_unit=UnitOfLength.METERS,
us_customary_unit=UnitOfLength.FEET, us_customary_unit=UnitOfLength.FEET,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
native_precision=0, suggested_display_precision=0,
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="CloudCover", key="CloudCover",

View file

@ -69,8 +69,8 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_CAQI, key=ATTR_API_CAQI,
icon="mdi:air-filter", icon="mdi:air-filter",
name=ATTR_API_CAQI, name=ATTR_API_CAQI,
native_precision=0,
native_unit_of_measurement="CAQI", native_unit_of_measurement="CAQI",
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LEVEL: data[ATTR_API_CAQI_LEVEL], ATTR_LEVEL: data[ATTR_API_CAQI_LEVEL],
ATTR_ADVICE: data[ATTR_API_ADVICE], ATTR_ADVICE: data[ATTR_API_ADVICE],
@ -81,17 +81,17 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_PM1, key=ATTR_API_PM1,
device_class=SensorDeviceClass.PM1, device_class=SensorDeviceClass.PM1,
name="PM1.0", name="PM1.0",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
), ),
AirlySensorEntityDescription( AirlySensorEntityDescription(
key=ATTR_API_PM25, key=ATTR_API_PM25,
device_class=SensorDeviceClass.PM25, device_class=SensorDeviceClass.PM25,
name="PM2.5", name="PM2.5",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_PM25}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_PM25}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_PM25}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_PM25}_{SUFFIX_PERCENT}"]),
@ -101,9 +101,9 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_PM10, key=ATTR_API_PM10,
device_class=SensorDeviceClass.PM10, device_class=SensorDeviceClass.PM10,
name=ATTR_API_PM10, name=ATTR_API_PM10,
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_PM10}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_PM10}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_PM10}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_PM10}_{SUFFIX_PERCENT}"]),
@ -113,32 +113,32 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_HUMIDITY, key=ATTR_API_HUMIDITY,
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
name=ATTR_API_HUMIDITY.capitalize(), name=ATTR_API_HUMIDITY.capitalize(),
native_precision=1,
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
), ),
AirlySensorEntityDescription( AirlySensorEntityDescription(
key=ATTR_API_PRESSURE, key=ATTR_API_PRESSURE,
device_class=SensorDeviceClass.PRESSURE, device_class=SensorDeviceClass.PRESSURE,
name=ATTR_API_PRESSURE.capitalize(), name=ATTR_API_PRESSURE.capitalize(),
native_precision=0,
native_unit_of_measurement=UnitOfPressure.HPA, native_unit_of_measurement=UnitOfPressure.HPA,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
), ),
AirlySensorEntityDescription( AirlySensorEntityDescription(
key=ATTR_API_TEMPERATURE, key=ATTR_API_TEMPERATURE,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name=ATTR_API_TEMPERATURE.capitalize(), name=ATTR_API_TEMPERATURE.capitalize(),
native_precision=1,
native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
), ),
AirlySensorEntityDescription( AirlySensorEntityDescription(
key=ATTR_API_CO, key=ATTR_API_CO,
name="Carbon monoxide", name="Carbon monoxide",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_CO}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_CO}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_CO}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_CO}_{SUFFIX_PERCENT}"]),
@ -148,9 +148,9 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_NO2, key=ATTR_API_NO2,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE, device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
name="Nitrogen dioxide", name="Nitrogen dioxide",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_NO2}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_NO2}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_NO2}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_NO2}_{SUFFIX_PERCENT}"]),
@ -160,9 +160,9 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_SO2, key=ATTR_API_SO2,
device_class=SensorDeviceClass.SULPHUR_DIOXIDE, device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
name="Sulphur dioxide", name="Sulphur dioxide",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_SO2}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_SO2}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_SO2}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_SO2}_{SUFFIX_PERCENT}"]),
@ -172,9 +172,9 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
key=ATTR_API_O3, key=ATTR_API_O3,
device_class=SensorDeviceClass.OZONE, device_class=SensorDeviceClass.OZONE,
name="Ozone", name="Ozone",
native_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: { attrs=lambda data: {
ATTR_LIMIT: data[f"{ATTR_API_O3}_{SUFFIX_LIMIT}"], ATTR_LIMIT: data[f"{ATTR_API_O3}_{SUFFIX_LIMIT}"],
ATTR_PERCENT: round(data[f"{ATTR_API_O3}_{SUFFIX_PERCENT}"]), ATTR_PERCENT: round(data[f"{ATTR_API_O3}_{SUFFIX_PERCENT}"]),

View file

@ -7,7 +7,7 @@ from dataclasses import dataclass
from datetime import date, datetime, timedelta, timezone from datetime import date, datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation as DecimalInvalidOperation from decimal import Decimal, InvalidOperation as DecimalInvalidOperation
import logging import logging
from math import ceil, floor, log10 from math import floor, log10
import re import re
from typing import Any, Final, cast, final from typing import Any, Final, cast, final
@ -138,10 +138,10 @@ class SensorEntityDescription(EntityDescription):
device_class: SensorDeviceClass | None = None device_class: SensorDeviceClass | None = None
last_reset: datetime | None = None last_reset: datetime | None = None
native_precision: int | None = None
native_unit_of_measurement: str | None = None native_unit_of_measurement: str | None = None
options: list[str] | None = None options: list[str] | None = None
state_class: SensorStateClass | str | None = None state_class: SensorStateClass | str | None = None
suggested_display_precision: int | None = None
suggested_unit_of_measurement: str | None = None suggested_unit_of_measurement: 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
@ -152,12 +152,12 @@ class SensorEntity(Entity):
entity_description: SensorEntityDescription entity_description: SensorEntityDescription
_attr_device_class: SensorDeviceClass | None _attr_device_class: SensorDeviceClass | None
_attr_last_reset: datetime | None _attr_last_reset: datetime | None
_attr_native_precision: int | 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_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_display_precision: int | None
_attr_suggested_unit_of_measurement: str | None _attr_suggested_unit_of_measurement: str | None
_attr_unit_of_measurement: None = ( _attr_unit_of_measurement: None = (
None # Subclasses of SensorEntity should not set this None # Subclasses of SensorEntity should not set this
@ -166,7 +166,7 @@ class SensorEntity(Entity):
_invalid_state_class_reported = False _invalid_state_class_reported = False
_invalid_unit_of_measurement_reported = False _invalid_unit_of_measurement_reported = False
_last_reset_reported = False _last_reset_reported = False
_sensor_option_precision: int | None = None _sensor_option_display_precision: int | None = None
_sensor_option_unit_of_measurement: str | None | UndefinedType = UNDEFINED _sensor_option_unit_of_measurement: str | None | UndefinedType = UNDEFINED
@callback @callback
@ -236,7 +236,8 @@ class SensorEntity(Entity):
await super().async_internal_added_to_hass() await super().async_internal_added_to_hass()
if not self.registry_entry: if not self.registry_entry:
return return
self.async_registry_entry_updated() self._async_read_entity_options()
self._update_suggested_precision()
@property @property
def device_class(self) -> SensorDeviceClass | None: def device_class(self) -> SensorDeviceClass | None:
@ -254,7 +255,7 @@ class SensorEntity(Entity):
if ( if (
self.state_class is not None self.state_class is not None
or self.native_unit_of_measurement is not None or self.native_unit_of_measurement is not None
or self.native_precision is not None or self.suggested_display_precision is not None
): ):
return True return True
# Sensors with custom device classes are not considered numeric # Sensors with custom device classes are not considered numeric
@ -359,59 +360,14 @@ class SensorEntity(Entity):
return self._attr_native_value return self._attr_native_value
@property @property
def native_precision(self) -> int | None: def suggested_display_precision(self) -> int | None:
"""Return the number of digits after the decimal point for the sensor's state. """Return the suggested number of decimal digits for display."""
if hasattr(self, "_attr_suggested_display_precision"):
If native_precision is None, no rounding is done unless the sensor is subject return self._attr_suggested_display_precision
to unit conversion.
The display precision is influenced by unit conversion, a sensor which has
native_unit_of_measurement 'Wh' and is converted to 'kWh' will have its
native_precision increased by 3.
"""
if hasattr(self, "_attr_native_precision"):
return self._attr_native_precision
if hasattr(self, "entity_description"): if hasattr(self, "entity_description"):
return self.entity_description.native_precision return self.entity_description.suggested_display_precision
return None return None
@final
@property
def precision(self) -> int | None:
"""Return the number of digits after the decimal point for the sensor's state.
This is the precision after unit conversion.
"""
# Highest priority, for registered entities: precision set by user
if self._sensor_option_precision is not None:
return self._sensor_option_precision
# Second priority, native precision
if (precision := self.native_precision) is None:
return None
device_class = self.device_class
native_unit_of_measurement = self.native_unit_of_measurement
unit_of_measurement = self.unit_of_measurement
if (
native_unit_of_measurement != unit_of_measurement
and device_class in UNIT_CONVERTERS
):
converter = UNIT_CONVERTERS[device_class]
# Scale the precision when converting to a larger or smaller unit
# For example 1.1 Wh should be rendered as 0.0011 kWh, not 0.0 kWh
ratio_log = log10(
converter.get_unit_ratio(
native_unit_of_measurement, unit_of_measurement
)
)
ratio_log = floor(ratio_log) if ratio_log > 0 else ceil(ratio_log)
precision = max(0, precision + ratio_log)
return precision
@property @property
def native_unit_of_measurement(self) -> str | None: def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of the sensor, if any.""" """Return the unit of measurement of the sensor, if any."""
@ -576,7 +532,7 @@ class SensorEntity(Entity):
) )
return value return value
precision = self.precision suggested_precision = self.suggested_display_precision
# If the sensor has neither a device class, a state class, a unit of measurement # If the sensor has neither a device class, a state class, a unit of measurement
# nor a precision then there are no further checks or conversions # nor a precision then there are no further checks or conversions
@ -593,13 +549,13 @@ class SensorEntity(Entity):
numerical_value = float(value) # type:ignore[arg-type] numerical_value = float(value) # type:ignore[arg-type]
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
# Raise if precision is not None, for other cases log a warning # Raise if precision is not None, for other cases log a warning
if precision is not None: if suggested_precision is not None:
raise ValueError( raise ValueError(
f"Sensor {self.entity_id} has device class {device_class}, " f"Sensor {self.entity_id} has device class {device_class}, "
f"state class {state_class} unit {unit_of_measurement} and " f"state class {state_class} unit {unit_of_measurement} and "
f"precision {precision} thus indicating it has a numeric value;" f"suggested precision {suggested_precision} thus indicating it "
f" however, it has the non-numeric value: {value} " f"has a numeric value; however, it has the non-numeric value: "
f"({type(value)})" f"{value} ({type(value)})"
) from err ) from err
# This should raise in Home Assistant Core 2023.4 # This should raise in Home Assistant Core 2023.4
if not self._invalid_numeric_value_reported: if not self._invalid_numeric_value_reported:
@ -629,7 +585,18 @@ class SensorEntity(Entity):
# Unit conversion needed # Unit conversion needed
converter = UNIT_CONVERTERS[device_class] converter = UNIT_CONVERTERS[device_class]
if precision is None: converted_numerical_value = UNIT_CONVERTERS[device_class].convert(
float(numerical_value),
native_unit_of_measurement,
unit_of_measurement,
)
# If unit conversion is happening, and there's no rounding for display,
# do a best effort rounding here.
if (
suggested_precision is None
and self._sensor_option_display_precision is None
):
# Deduce the precision by finding the decimal point, if any # Deduce the precision by finding the decimal point, if any
value_s = str(value) value_s = str(value)
precision = ( precision = (
@ -648,20 +615,10 @@ class SensorEntity(Entity):
) )
precision = precision + floor(ratio_log) precision = precision + floor(ratio_log)
converted_numerical_value = converter.convert(
float(numerical_value),
native_unit_of_measurement,
unit_of_measurement,
)
value = f"{converted_numerical_value:.{precision}f}" value = f"{converted_numerical_value:.{precision}f}"
# This can be replaced with adding the z option when we drop support for # This can be replaced with adding the z option when we drop support for
# Python 3.10 # Python 3.10
value = NEGATIVE_ZERO_PATTERN.sub(r"\1", value) value = NEGATIVE_ZERO_PATTERN.sub(r"\1", value)
elif precision is not None:
value = f"{numerical_value:.{precision}f}"
# This can be replaced with adding the z option when we drop support for
# Python 3.10
value = NEGATIVE_ZERO_PATTERN.sub(r"\1", value)
# Validate unit of measurement used for sensors with a device class # Validate unit of measurement used for sensors with a device class
if ( if (
@ -703,15 +660,35 @@ class SensorEntity(Entity):
return super().__repr__() return super().__repr__()
def _custom_precision_or_none(self) -> int | None: def _suggested_precision_or_none(self) -> int | None:
"""Return a custom precisions or None if not set.""" """Return suggested display precision, or None if not set."""
assert self.registry_entry assert self.registry_entry
if (sensor_options := self.registry_entry.options.get(DOMAIN)) and ( if (sensor_options := self.registry_entry.options.get(DOMAIN)) and (
precision := sensor_options.get("precision") precision := sensor_options.get("suggested_display_precision")
) is not None: ) is not None:
return int(precision) return cast(int, precision)
return None return None
def _update_suggested_precision(self) -> None:
"""Update suggested display precision stored in registry."""
assert self.registry_entry
display_precision = self.suggested_display_precision
if (
sensor_options := self.registry_entry.options.get(DOMAIN, {})
) and sensor_options.get("suggested_display_precision") == display_precision:
return
registry = er.async_get(self.hass)
sensor_options = dict(sensor_options)
sensor_options.pop("suggested_display_precision", None)
if display_precision is not None:
sensor_options["suggested_display_precision"] = display_precision
registry.async_update_entity_options(
self.entity_id, DOMAIN, sensor_options or None
)
def _custom_unit_or_undef( def _custom_unit_or_undef(
self, primary_key: str, secondary_key: str self, primary_key: str, secondary_key: str
) -> str | None | UndefinedType: ) -> str | None | UndefinedType:
@ -732,7 +709,16 @@ class SensorEntity(Entity):
@callback @callback
def async_registry_entry_updated(self) -> None: def async_registry_entry_updated(self) -> None:
"""Run when the entity registry entry has been updated.""" """Run when the entity registry entry has been updated."""
self._sensor_option_precision = self._custom_precision_or_none() self._async_read_entity_options()
@callback
def _async_read_entity_options(self) -> None:
"""Read entity options from entity registry.
Called when the entity registry entry has been updated and before the sensor is
added to the state machine.
"""
self._sensor_option_display_precision = self._suggested_precision_or_none()
assert self.registry_entry assert self.registry_entry
if ( if (
sensor_options := self.registry_entry.options.get(f"{DOMAIN}.private") sensor_options := self.registry_entry.options.get(f"{DOMAIN}.private")

View file

@ -44,7 +44,7 @@ async def test_sensor_without_forecast(hass):
state = hass.states.get("sensor.home_cloud_ceiling") state = hass.states.get("sensor.home_cloud_ceiling")
assert state assert state
assert state.state == "3200" assert state.state == "3200.0"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog" assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog"
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.METERS assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.METERS
@ -54,6 +54,7 @@ async def test_sensor_without_forecast(hass):
entry = registry.async_get("sensor.home_cloud_ceiling") entry = registry.async_get("sensor.home_cloud_ceiling")
assert entry assert entry
assert entry.unique_id == "0123456-ceiling" assert entry.unique_id == "0123456-ceiling"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_precipitation") state = hass.states.get("sensor.home_precipitation")
assert state assert state
@ -665,7 +666,7 @@ async def test_availability(hass):
state = hass.states.get("sensor.home_cloud_ceiling") state = hass.states.get("sensor.home_cloud_ceiling")
assert state assert state
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "3200" assert state.state == "3200.0"
future = utcnow() + timedelta(minutes=60) future = utcnow() + timedelta(minutes=60)
with patch( with patch(
@ -696,7 +697,7 @@ async def test_availability(hass):
state = hass.states.get("sensor.home_cloud_ceiling") state = hass.states.get("sensor.home_cloud_ceiling")
assert state assert state
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "3200" assert state.state == "3200.0"
async def test_manual_update_entity(hass): async def test_manual_update_entity(hass):
@ -736,7 +737,7 @@ async def test_sensor_imperial_units(hass):
state = hass.states.get("sensor.home_cloud_ceiling") state = hass.states.get("sensor.home_cloud_ceiling")
assert state assert state
assert state.state == "10500" assert state.state == "10500.0"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog" assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog"
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.FEET assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.FEET
@ -749,7 +750,7 @@ async def test_state_update(hass):
state = hass.states.get("sensor.home_cloud_ceiling") state = hass.states.get("sensor.home_cloud_ceiling")
assert state assert state
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "3200" assert state.state == "3200.0"
future = utcnow() + timedelta(minutes=60) future = utcnow() + timedelta(minutes=60)

View file

@ -28,7 +28,7 @@ async def test_async_setup_entry(hass, aioclient_mock):
state = hass.states.get("sensor.home_pm2_5") state = hass.states.get("sensor.home_pm2_5")
assert state is not None assert state is not None
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "4" assert state.state == "4.37"
async def test_config_not_ready(hass, aioclient_mock): async def test_config_not_ready(hass, aioclient_mock):

View file

@ -38,7 +38,7 @@ async def test_sensor(hass, aioclient_mock):
state = hass.states.get("sensor.home_caqi") state = hass.states.get("sensor.home_caqi")
assert state assert state
assert state.state == "7" assert state.state == "7.29"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "CAQI" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "CAQI"
assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" assert state.attributes.get(ATTR_ICON) == "mdi:air-filter"
@ -46,10 +46,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_caqi") entry = registry.async_get("sensor.home_caqi")
assert entry assert entry
assert entry.unique_id == "123-456-caqi" assert entry.unique_id == "123-456-caqi"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_humidity") state = hass.states.get("sensor.home_humidity")
assert state assert state
assert state.state == "68.3" assert state.state == "68.35"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.HUMIDITY assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.HUMIDITY
@ -58,10 +59,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_humidity") entry = registry.async_get("sensor.home_humidity")
assert entry assert entry
assert entry.unique_id == "123-456-humidity" assert entry.unique_id == "123-456-humidity"
assert entry.options["sensor"] == {"suggested_display_precision": 1}
state = hass.states.get("sensor.home_pm1_0") state = hass.states.get("sensor.home_pm1_0")
assert state assert state
assert state.state == "3" assert state.state == "2.83"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -73,10 +75,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_pm1_0") entry = registry.async_get("sensor.home_pm1_0")
assert entry assert entry
assert entry.unique_id == "123-456-pm1" assert entry.unique_id == "123-456-pm1"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_pm2_5") state = hass.states.get("sensor.home_pm2_5")
assert state assert state
assert state.state == "4" assert state.state == "4.37"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -88,10 +91,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_pm2_5") entry = registry.async_get("sensor.home_pm2_5")
assert entry assert entry
assert entry.unique_id == "123-456-pm25" assert entry.unique_id == "123-456-pm25"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_pm10") state = hass.states.get("sensor.home_pm10")
assert state assert state
assert state.state == "6" assert state.state == "6.06"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -103,16 +107,18 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_pm10") entry = registry.async_get("sensor.home_pm10")
assert entry assert entry
assert entry.unique_id == "123-456-pm10" assert entry.unique_id == "123-456-pm10"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_carbon_monoxide") state = hass.states.get("sensor.home_carbon_monoxide")
assert state assert state
assert state.state == "162" assert state.state == "162.49"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
) )
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
assert entry.options["sensor"] == {"suggested_display_precision": 0}
entry = registry.async_get("sensor.home_carbon_monoxide") entry = registry.async_get("sensor.home_carbon_monoxide")
assert entry assert entry
@ -120,7 +126,7 @@ async def test_sensor(hass, aioclient_mock):
state = hass.states.get("sensor.home_nitrogen_dioxide") state = hass.states.get("sensor.home_nitrogen_dioxide")
assert state assert state
assert state.state == "16" assert state.state == "16.04"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -132,10 +138,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_nitrogen_dioxide") entry = registry.async_get("sensor.home_nitrogen_dioxide")
assert entry assert entry
assert entry.unique_id == "123-456-no2" assert entry.unique_id == "123-456-no2"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_ozone") state = hass.states.get("sensor.home_ozone")
assert state assert state
assert state.state == "42" assert state.state == "41.52"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -147,10 +154,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_ozone") entry = registry.async_get("sensor.home_ozone")
assert entry assert entry
assert entry.unique_id == "123-456-o3" assert entry.unique_id == "123-456-o3"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_sulphur_dioxide") state = hass.states.get("sensor.home_sulphur_dioxide")
assert state assert state
assert state.state == "14" assert state.state == "13.97"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert ( assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@ -162,10 +170,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_sulphur_dioxide") entry = registry.async_get("sensor.home_sulphur_dioxide")
assert entry assert entry
assert entry.unique_id == "123-456-so2" assert entry.unique_id == "123-456-so2"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_pressure") state = hass.states.get("sensor.home_pressure")
assert state assert state
assert state.state == "1020" assert state.state == "1019.86"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.HPA assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.HPA
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE
@ -174,10 +183,11 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_pressure") entry = registry.async_get("sensor.home_pressure")
assert entry assert entry
assert entry.unique_id == "123-456-pressure" assert entry.unique_id == "123-456-pressure"
assert entry.options["sensor"] == {"suggested_display_precision": 0}
state = hass.states.get("sensor.home_temperature") state = hass.states.get("sensor.home_temperature")
assert state assert state
assert state.state == "14.4" assert state.state == "14.37"
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
@ -186,6 +196,7 @@ async def test_sensor(hass, aioclient_mock):
entry = registry.async_get("sensor.home_temperature") entry = registry.async_get("sensor.home_temperature")
assert entry assert entry
assert entry.unique_id == "123-456-temperature" assert entry.unique_id == "123-456-temperature"
assert entry.options["sensor"] == {"suggested_display_precision": 1}
async def test_availability(hass, aioclient_mock): async def test_availability(hass, aioclient_mock):
@ -195,7 +206,7 @@ async def test_availability(hass, aioclient_mock):
state = hass.states.get("sensor.home_humidity") state = hass.states.get("sensor.home_humidity")
assert state assert state
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "68.3" assert state.state == "68.35"
aioclient_mock.clear_requests() aioclient_mock.clear_requests()
aioclient_mock.get( aioclient_mock.get(
@ -218,7 +229,7 @@ async def test_availability(hass, aioclient_mock):
state = hass.states.get("sensor.home_humidity") state = hass.states.get("sensor.home_humidity")
assert state assert state
assert state.state != STATE_UNAVAILABLE assert state.state != STATE_UNAVAILABLE
assert state.state == "68.3" assert state.state == "68.35"
async def test_manual_update_entity(hass, aioclient_mock): async def test_manual_update_entity(hass, aioclient_mock):

View file

@ -518,195 +518,6 @@ async def test_custom_unit(
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == state_unit assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == state_unit
@pytest.mark.parametrize(
"device_class,native_unit,custom_unit,native_value,native_precision,default_state,custom_state",
[
(
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
UnitOfPressure.HPA,
UnitOfPressure.INHG,
1000.0,
2,
"1000.00", # Native precision is 2
"29.530", # One digit of precision added when converting
),
(
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
UnitOfPressure.INHG,
UnitOfPressure.HPA,
29.9211,
3,
"29.921", # Native precision is 3
"1013.24", # One digit of precision removed when converting
),
(
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
UnitOfPressure.INHG,
UnitOfPressure.HPA,
-0.0001,
3,
"0.000", # Native precision is 3
"0.00", # One digit of precision removed when converting
),
],
)
async def test_native_precision_scaling(
hass,
enable_custom_integrations,
device_class,
native_unit,
custom_unit,
native_value,
native_precision,
default_state,
custom_state,
):
"""Test native precision is influenced by unit conversion."""
entity_registry = er.async_get(hass)
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
native_value=str(native_value),
native_precision=native_precision,
native_unit_of_measurement=native_unit,
device_class=device_class,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
state = hass.states.get(entity0.entity_id)
assert state.state == default_state
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
entity_registry.async_update_entity_options(
entry.entity_id, "sensor", {"unit_of_measurement": custom_unit}
)
await hass.async_block_till_done()
state = hass.states.get(entity0.entity_id)
assert state.state == custom_state
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
@pytest.mark.parametrize(
"device_class,native_unit,custom_precision,native_value,default_state,custom_state",
[
(
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
UnitOfPressure.HPA,
4,
1000.0,
"1000.000",
"1000.0000",
),
(
SensorDeviceClass.DISTANCE,
UnitOfLength.KILOMETERS,
1,
-0.04,
"-0.040",
"0.0", # Make sure minus is dropped
),
],
)
async def test_custom_precision_native_precision(
hass,
enable_custom_integrations,
device_class,
native_unit,
custom_precision,
native_value,
default_state,
custom_state,
):
"""Test custom precision."""
entity_registry = er.async_get(hass)
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
native_value=str(native_value),
native_precision=3,
native_unit_of_measurement=native_unit,
device_class=device_class,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
state = hass.states.get(entity0.entity_id)
assert state.state == default_state
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
entity_registry.async_update_entity_options(
entry.entity_id, "sensor", {"precision": custom_precision}
)
await hass.async_block_till_done()
state = hass.states.get(entity0.entity_id)
assert state.state == custom_state
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
@pytest.mark.parametrize(
"device_class,native_unit,custom_precision,native_value,custom_state",
[
(
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
UnitOfPressure.HPA,
4,
1000.0,
"1000.0000",
),
],
)
async def test_custom_precision_no_native_precision(
hass,
enable_custom_integrations,
device_class,
native_unit,
custom_precision,
native_value,
custom_state,
):
"""Test custom precision."""
entity_registry = er.async_get(hass)
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
entity_registry.async_update_entity_options(
entry.entity_id, "sensor", {"precision": custom_precision}
)
await hass.async_block_till_done()
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
native_value=str(native_value),
native_unit_of_measurement=native_unit,
device_class=device_class,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
state = hass.states.get(entity0.entity_id)
assert state.state == custom_state
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
@pytest.mark.parametrize( @pytest.mark.parametrize(
"native_unit, custom_unit, state_unit, native_value, native_state, custom_state, device_class", "native_unit, custom_unit, state_unit, native_value, native_state, custom_state, device_class",
[ [
@ -1188,6 +999,121 @@ async def test_unit_conversion_priority_suggested_unit_change(
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit
@pytest.mark.parametrize(
"native_unit, suggested_precision, native_value, device_class",
[
# Distance
(
UnitOfLength.KILOMETERS,
4,
1000,
SensorDeviceClass.DISTANCE,
),
],
)
async def test_suggested_precision_option(
hass,
enable_custom_integrations,
native_unit,
suggested_precision,
native_value,
device_class,
):
"""Test suggested precision is stored in the registry."""
entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
device_class=device_class,
native_unit_of_measurement=native_unit,
native_value=str(native_value),
suggested_display_precision=suggested_precision,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
# Assert the suggested precision is stored in the registry
entry = entity_registry.async_get(entity0.entity_id)
assert entry.options == {
"sensor": {"suggested_display_precision": suggested_precision}
}
@pytest.mark.parametrize(
"native_unit, old_precision, new_precision, native_value, device_class",
[
(
UnitOfLength.KILOMETERS,
4,
1,
1000,
SensorDeviceClass.DISTANCE,
),
],
)
async def test_suggested_precision_option_update(
hass,
enable_custom_integrations,
native_unit,
old_precision,
new_precision,
native_value,
device_class,
):
"""Test suggested precision stored in the registry is updated."""
entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
# Pre-register entities
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
entity_registry.async_update_entity_options(
entry.entity_id,
"sensor",
{
"suggested_display_precision": old_precision,
},
)
entity_registry.async_update_entity_options(
entry.entity_id,
"sensor.private",
{
"suggested_unit_of_measurement": native_unit,
},
)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
device_class=device_class,
native_unit_of_measurement=native_unit,
native_value=str(native_value),
suggested_display_precision=new_precision,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
# Assert the suggested precision is stored in the registry
entry = entity_registry.async_get(entity0.entity_id)
assert entry.options == {
"sensor": {
"suggested_display_precision": new_precision,
},
"sensor.private": {
"suggested_unit_of_measurement": native_unit,
},
}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"unit_system, native_unit, original_unit, native_value, original_value, device_class", "unit_system, native_unit, original_unit, native_value, original_value, device_class",
[ [
@ -1519,10 +1445,10 @@ async def test_non_numeric_validation_raise(
platform.ENTITIES["0"] = platform.MockSensor( platform.ENTITIES["0"] = platform.MockSensor(
name="Test", name="Test",
device_class=device_class, device_class=device_class,
native_precision=precision,
native_unit_of_measurement=unit, native_unit_of_measurement=unit,
native_value=native_value, native_value=native_value,
state_class=state_class, state_class=state_class,
suggested_display_precision=precision,
) )
entity0 = platform.ENTITIES["0"] entity0 = platform.ENTITIES["0"]
@ -1650,7 +1576,7 @@ async def test_device_classes_with_invalid_state_class(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device_class,state_class,native_unit_of_measurement,native_precision,is_numeric", "device_class,state_class,native_unit_of_measurement,suggested_precision,is_numeric",
[ [
(SensorDeviceClass.ENUM, None, None, None, False), (SensorDeviceClass.ENUM, None, None, None, False),
(SensorDeviceClass.DATE, None, None, None, False), (SensorDeviceClass.DATE, None, None, None, False),
@ -1669,7 +1595,7 @@ async def test_numeric_state_expected_helper(
device_class: SensorDeviceClass | None, device_class: SensorDeviceClass | None,
state_class: SensorStateClass | None, state_class: SensorStateClass | None,
native_unit_of_measurement: str | None, native_unit_of_measurement: str | None,
native_precision: int | None, suggested_precision: int | None,
is_numeric: bool, is_numeric: bool,
) -> None: ) -> None:
"""Test numeric_state_expected helper.""" """Test numeric_state_expected helper."""
@ -1681,7 +1607,7 @@ async def test_numeric_state_expected_helper(
device_class=device_class, device_class=device_class,
state_class=state_class, state_class=state_class,
native_unit_of_measurement=native_unit_of_measurement, native_unit_of_measurement=native_unit_of_measurement,
native_precision=native_precision, suggested_display_precision=suggested_precision,
) )
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})

View file

@ -98,9 +98,9 @@ class MockSensor(MockEntity, SensorEntity):
return self._handle("last_reset") return self._handle("last_reset")
@property @property
def native_precision(self): def suggested_display_precision(self):
"""Return the number of digits after the decimal point.""" """Return the number of digits after the decimal point."""
return self._handle("native_precision") return self._handle("suggested_display_precision")
@property @property
def native_unit_of_measurement(self): def native_unit_of_measurement(self):