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 decimal import Decimal, InvalidOperation as DecimalInvalidOperation
import logging
from math import floor, log10
from math import ceil, floor, log10
import re
from typing import Any, Final, cast, final
@ -679,11 +679,40 @@ class SensorEntity(Entity):
"""Update suggested display precision stored in registry."""
assert self.registry_entry
device_class = self.device_class
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 (
sensor_options := self.registry_entry.options.get(DOMAIN, {})
) and sensor_options.get("suggested_display_precision") == display_precision:
display_precision is not None
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
registry = er.async_get(self.hass)
@ -716,6 +745,7 @@ class SensorEntity(Entity):
def async_registry_entry_updated(self) -> None:
"""Run when the entity registry entry has been updated."""
self._async_read_entity_options()
self._update_suggested_precision()
@callback
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(
"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
(
METRIC_SYSTEM,
UnitOfLength.KILOMETERS,
4,
4,
1000,
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(
hass,
enable_custom_integrations,
unit_system,
native_unit,
suggested_precision,
integration_suggested_precision,
options_suggested_precision,
native_value,
device_class,
extra_options,
):
"""Test suggested precision is stored in the registry."""
hass.config.units = unit_system
entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
@ -1173,7 +1192,7 @@ async def test_suggested_precision_option(
device_class=device_class,
native_unit_of_measurement=native_unit,
native_value=str(native_value),
suggested_display_precision=suggested_precision,
suggested_display_precision=integration_suggested_precision,
unique_id="very_unique",
)
entity0 = platform.ENTITIES["0"]
@ -1183,34 +1202,58 @@ async def test_suggested_precision_option(
# 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}
assert entry.options == extra_options | {
"sensor": {"suggested_display_precision": options_suggested_precision}
}
@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,
4,
1,
1,
1000,
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(
hass,
enable_custom_integrations,
unit_system,
native_unit,
suggested_unit,
old_precision,
new_precision,
opt_precision,
native_value,
device_class,
extra_options,
):
"""Test suggested precision stored in the registry is updated."""
hass.config.units = unit_system
entity_registry = er.async_get(hass)
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
@ -1228,7 +1271,7 @@ async def test_suggested_precision_option_update(
entry.entity_id,
"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)
assert entry.options == {
"sensor": {
"suggested_display_precision": new_precision,
"suggested_display_precision": opt_precision,
},
"sensor.private": {
"suggested_unit_of_measurement": native_unit,
"suggested_unit_of_measurement": suggested_unit,
},
}