Adjust suggested_display_precision according to unit conversion (#87614)

This commit is contained in:
Erik Montnemery 2023-02-13 13:55:12 +01:00 committed by GitHub
parent ea29cdfe83
commit 8f2a764a43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 12 deletions

View File

@ -8,7 +8,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 floor, log10 from math import ceil, floor, log10
import re import re
from typing import Any, Final, cast, final from typing import Any, Final, cast, final
@ -679,11 +679,40 @@ class SensorEntity(Entity):
"""Update suggested display precision stored in registry.""" """Update suggested display precision stored in registry."""
assert self.registry_entry assert self.registry_entry
device_class = self.device_class
display_precision = self.suggested_display_precision display_precision = self.suggested_display_precision
default_unit_of_measurement = (
self.suggested_unit_of_measurement or self.native_unit_of_measurement
)
unit_of_measurement = self.unit_of_measurement
if ( if (
sensor_options := self.registry_entry.options.get(DOMAIN, {}) display_precision is not None
) and sensor_options.get("suggested_display_precision") == display_precision: and default_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(
default_unit_of_measurement, unit_of_measurement
)
)
ratio_log = floor(ratio_log) if ratio_log > 0 else ceil(ratio_log)
display_precision = max(0, display_precision + ratio_log)
if display_precision is None and (
DOMAIN not in self.registry_entry.options
or "suggested_display_precision" not in self.registry_entry.options
):
return
sensor_options = self.registry_entry.options.get(DOMAIN, {})
if (
"suggested_display_precision" in sensor_options
and sensor_options["suggested_display_precision"] == display_precision
):
return return
registry = er.async_get(self.hass) registry = er.async_get(self.hass)
@ -716,6 +745,7 @@ class SensorEntity(Entity):
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._async_read_entity_options() self._async_read_entity_options()
self._update_suggested_precision()
@callback @callback
def _async_read_entity_options(self) -> None: def _async_read_entity_options(self) -> None:

View File

@ -1143,27 +1143,46 @@ async def test_unit_conversion_priority_suggested_unit_change(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"native_unit, suggested_precision, native_value, device_class", "unit_system, native_unit, integration_suggested_precision,"
"options_suggested_precision, native_value, device_class, extra_options",
[ [
# Distance # Distance
( (
METRIC_SYSTEM,
UnitOfLength.KILOMETERS, UnitOfLength.KILOMETERS,
4, 4,
4,
1000, 1000,
SensorDeviceClass.DISTANCE, SensorDeviceClass.DISTANCE,
{},
),
# Air pressure
(
US_CUSTOMARY_SYSTEM,
UnitOfPressure.HPA,
0,
1,
1000,
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
{"sensor.private": {"suggested_unit_of_measurement": "inHg"}},
), ),
], ],
) )
async def test_suggested_precision_option( async def test_suggested_precision_option(
hass, hass,
enable_custom_integrations, enable_custom_integrations,
unit_system,
native_unit, native_unit,
suggested_precision, integration_suggested_precision,
options_suggested_precision,
native_value, native_value,
device_class, device_class,
extra_options,
): ):
"""Test suggested precision is stored in the registry.""" """Test suggested precision is stored in the registry."""
hass.config.units = unit_system
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor") platform = getattr(hass.components, "test.sensor")
platform.init(empty=True) platform.init(empty=True)
@ -1173,7 +1192,7 @@ async def test_suggested_precision_option(
device_class=device_class, device_class=device_class,
native_unit_of_measurement=native_unit, native_unit_of_measurement=native_unit,
native_value=str(native_value), native_value=str(native_value),
suggested_display_precision=suggested_precision, suggested_display_precision=integration_suggested_precision,
unique_id="very_unique", unique_id="very_unique",
) )
entity0 = platform.ENTITIES["0"] entity0 = platform.ENTITIES["0"]
@ -1183,34 +1202,58 @@ async def test_suggested_precision_option(
# Assert the suggested precision is stored in the registry # Assert the suggested precision is stored in the registry
entry = entity_registry.async_get(entity0.entity_id) entry = entity_registry.async_get(entity0.entity_id)
assert entry.options == { assert entry.options == extra_options | {
"sensor": {"suggested_display_precision": suggested_precision} "sensor": {"suggested_display_precision": options_suggested_precision}
} }
@pytest.mark.parametrize( @pytest.mark.parametrize(
"native_unit, old_precision, new_precision, native_value, device_class", "unit_system, native_unit, suggested_unit, old_precision, new_precision,"
"opt_precision, native_value, device_class, extra_options",
[ [
# Distance
( (
METRIC_SYSTEM,
UnitOfLength.KILOMETERS,
UnitOfLength.KILOMETERS, UnitOfLength.KILOMETERS,
4, 4,
1, 1,
1,
1000, 1000,
SensorDeviceClass.DISTANCE, SensorDeviceClass.DISTANCE,
{},
),
# Air pressure
(
US_CUSTOMARY_SYSTEM,
UnitOfPressure.HPA,
UnitOfPressure.INHG,
1,
1,
2,
1000,
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
{"sensor.private": {"suggested_unit_of_measurement": "inHg"}},
), ),
], ],
) )
async def test_suggested_precision_option_update( async def test_suggested_precision_option_update(
hass, hass,
enable_custom_integrations, enable_custom_integrations,
unit_system,
native_unit, native_unit,
suggested_unit,
old_precision, old_precision,
new_precision, new_precision,
opt_precision,
native_value, native_value,
device_class, device_class,
extra_options,
): ):
"""Test suggested precision stored in the registry is updated.""" """Test suggested precision stored in the registry is updated."""
hass.config.units = unit_system
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor") platform = getattr(hass.components, "test.sensor")
platform.init(empty=True) platform.init(empty=True)
@ -1228,7 +1271,7 @@ async def test_suggested_precision_option_update(
entry.entity_id, entry.entity_id,
"sensor.private", "sensor.private",
{ {
"suggested_unit_of_measurement": native_unit, "suggested_unit_of_measurement": suggested_unit,
}, },
) )
@ -1249,10 +1292,10 @@ async def test_suggested_precision_option_update(
entry = entity_registry.async_get(entity0.entity_id) entry = entity_registry.async_get(entity0.entity_id)
assert entry.options == { assert entry.options == {
"sensor": { "sensor": {
"suggested_display_precision": new_precision, "suggested_display_precision": opt_precision,
}, },
"sensor.private": { "sensor.private": {
"suggested_unit_of_measurement": native_unit, "suggested_unit_of_measurement": suggested_unit,
}, },
} }